You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by Howard Lewis Ship <hl...@gmail.com> on 2011/06/19 21:03:14 UTC

Client-side Pub/Sub vs. Memory Leaks

I've been, effectively, prototyping a client-side pub/sub solution for
Tapestry with one of my client projects.

The goal is to reduce the abuse of DOM events or other complicated
structure.  If two components need to work together when they are both
present in the page, it can be difficult to orchestrate when those
components render at different times (due to Ajax) or are nested
within other components (making it hard to obtain a client-side id to
work with). Having behaviors loosely coupled, using well-known topic
names, is a major part of the solution for this.

Topics names are of the form "tapestry:zone-updated/myZoneId" or
"app:master-category-changed".  The convention I'm coming up with is
that topic names start with a prefix ("tapestry:" for the framework,
or "app:" for the application), then a name, then optionally a
client-element id.

Message publication is by topic name, and there's a bubbling system: a
message published to "foo/bar" can be subscribed to with "foo/bar" or
"foo".  This will allow, for example, code to be aware of any form
that's about to submit, or any Zone that's about to update, etc.

The topic message payload (or just "message") is any JavaScript
object, defined by the code that publishes to the topic.  It is passed
to any listener functions listening to the topic.

Part of the code is in 5.3.0, part of it is evolving inside my client
project and will be merged into 5.3.x.

In any case, lots of it is working out nicely. I have some very
sophisticated logic on the client side and the pubsub system makes
things possible that otherwise would not be.

However, I do have a concern.  Although its easy for a listener to
unsubscribe itself, what I'm struggling with is when a topic is
associated with a DOM element (which is often the case).  I would like
to have a means of recognizing that a DOM element (or tree of DOM
elements) has been removed from the DOM, and to automatically destroy
topic publishers and listeners related to that DOM element.

Failure to do this will likely result in memory leaks inside
long-running pages, and potentially to client-side exceptions as
listeners perform updates to DOM elements that are no longer attached
to the document.

One option I'm considering is to make topic names simple strings, but
to always associate them with a DOM element.  I.e.:

var publisher = T5.createPublisher("tapestry:form-validate", myForm);
// createPublisher() returns a publisher function.
// The return value of the publisher is an array of the return values
of all listener functions.

T5.sub({ topic: "tapestry:form-validate", element: myForm}, function()
{ ... }); // to match message published for myForm
// The return value of sub() is a function used to unsubscribe the
listener function from the topic. This can be used for
// "listen once" scenarios.

T5.sub("tapestry:form-validate", function() { ... }); // to match
messages published for any form

var result = publisher({ foo:bar, biff:baz}); // Send the object to
listener functions
// Get back an array of return values.

This would replace and formalize the "foo/client-id" pattern I discuss above.

The pub/sub approach above could combine with some code that would
iterate over elements being removed form the DOM and cleanup the
publishers/listeners mappings.  Part of this code already exists, to
clean things up for IE (breaking apart the DOM and JS to prevent
memory leaks).

As I write this, I'm really liking the idea of always linking topics
to elements; listener functions could receive two parameters: the
payload message, and (second & optional) an event object that would
include, among other things, a reference to the element for the
message.

In the larger scheme of things, appling this pub/sub structure will
have a lot of benefits.  It effectively provides more "seams" on the
client-side where logic can be injected, which should lead to less
monkey-patching. I also see it as a great mechanism for dealing with
Cometd/Websockets, where the server-side can publish events to the
client-side.

My personal plan is to start introducing the T5 namespace and pub/sub
features in 5.3 and go a bit more whole-hog in 5.4.  In fact, the
"theme" of 5.4 could be JavaScript: breaking the ties on Prototype,
pub/sub, and Websocket support.

So, I'm interested in general thoughts on the approach, and also some
insight into the memory leak issue.  It would be great if there was a
bubbling DOM event that we could use for this purpose. I don't think
there is.

-- 
Howard M. Lewis Ship

Creator of Apache Tapestry

The source for Tapestry training, mentoring and support. Contact me to
learn how I can get you up and productive in Tapestry fast!

(971) 678-5210
http://howardlewisship.com

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org


Re: Client-side Pub/Sub vs. Memory Leaks

Posted by Robin Komiwes <ro...@apache.org>.
Well, it's a nice idea, and as you said, it 's a great way to offer
extensions points on a system.

About the memory leak problem, what about overriding/decorating the native
removeChild method (or any other native instruction to destroy nodes) ?
Something like:
HTMLElement.prototype._removeChild = HTMLElement.prototype.removeChild;
HTMLElement.prototype.removeChild = function( e ) {
console.log('hello!');
return this._removeChild( e );
};


On the general features of the pub/sub system, have you thought about:
- stopping the event and/or preventing bubbling, when returning false for
example
- events ordering, in a way like the Tapestry OrderedConfiguration class.

--
Robin


On Sun, Jun 19, 2011 at 11:07 PM, Kalle Korhonen <kalle.o.korhonen@gmail.com
> wrote:

> Tying to dom elements sounds reasonable to me. You mean
> DOMNodeRemoved? There's such as a thing in the spec but do the
> browsers, especially everybody's favorite trouble maker, IE, support
> it?  See http://en.wikipedia.org/wiki/DOM_events,
>
> http://stackoverflow.com/questions/3750546/event-observe-domnoderemoved-does-not-work-in-ie
> and
> http://www.bennadel.com/blog/1623-Ask-Ben-Detecting-When-DOM-Elements-Have-Been-Removed-With-jQuery.htm
> .
>
> Kalle
>
>
> On Sun, Jun 19, 2011 at 12:03 PM, Howard Lewis Ship <hl...@gmail.com>
> wrote:
> > I've been, effectively, prototyping a client-side pub/sub solution for
> > Tapestry with one of my client projects.
> >
> > The goal is to reduce the abuse of DOM events or other complicated
> > structure.  If two components need to work together when they are both
> > present in the page, it can be difficult to orchestrate when those
> > components render at different times (due to Ajax) or are nested
> > within other components (making it hard to obtain a client-side id to
> > work with). Having behaviors loosely coupled, using well-known topic
> > names, is a major part of the solution for this.
> >
> > Topics names are of the form "tapestry:zone-updated/myZoneId" or
> > "app:master-category-changed".  The convention I'm coming up with is
> > that topic names start with a prefix ("tapestry:" for the framework,
> > or "app:" for the application), then a name, then optionally a
> > client-element id.
> >
> > Message publication is by topic name, and there's a bubbling system: a
> > message published to "foo/bar" can be subscribed to with "foo/bar" or
> > "foo".  This will allow, for example, code to be aware of any form
> > that's about to submit, or any Zone that's about to update, etc.
> >
> > The topic message payload (or just "message") is any JavaScript
> > object, defined by the code that publishes to the topic.  It is passed
> > to any listener functions listening to the topic.
> >
> > Part of the code is in 5.3.0, part of it is evolving inside my client
> > project and will be merged into 5.3.x.
> >
> > In any case, lots of it is working out nicely. I have some very
> > sophisticated logic on the client side and the pubsub system makes
> > things possible that otherwise would not be.
> >
> > However, I do have a concern.  Although its easy for a listener to
> > unsubscribe itself, what I'm struggling with is when a topic is
> > associated with a DOM element (which is often the case).  I would like
> > to have a means of recognizing that a DOM element (or tree of DOM
> > elements) has been removed from the DOM, and to automatically destroy
> > topic publishers and listeners related to that DOM element.
> >
> > Failure to do this will likely result in memory leaks inside
> > long-running pages, and potentially to client-side exceptions as
> > listeners perform updates to DOM elements that are no longer attached
> > to the document.
> >
> > One option I'm considering is to make topic names simple strings, but
> > to always associate them with a DOM element.  I.e.:
> >
> > var publisher = T5.createPublisher("tapestry:form-validate", myForm);
> > // createPublisher() returns a publisher function.
> > // The return value of the publisher is an array of the return values
> > of all listener functions.
> >
> > T5.sub({ topic: "tapestry:form-validate", element: myForm}, function()
> > { ... }); // to match message published for myForm
> > // The return value of sub() is a function used to unsubscribe the
> > listener function from the topic. This can be used for
> > // "listen once" scenarios.
> >
> > T5.sub("tapestry:form-validate", function() { ... }); // to match
> > messages published for any form
> >
> > var result = publisher({ foo:bar, biff:baz}); // Send the object to
> > listener functions
> > // Get back an array of return values.
> >
> > This would replace and formalize the "foo/client-id" pattern I discuss
> above.
> >
> > The pub/sub approach above could combine with some code that would
> > iterate over elements being removed form the DOM and cleanup the
> > publishers/listeners mappings.  Part of this code already exists, to
> > clean things up for IE (breaking apart the DOM and JS to prevent
> > memory leaks).
> >
> > As I write this, I'm really liking the idea of always linking topics
> > to elements; listener functions could receive two parameters: the
> > payload message, and (second & optional) an event object that would
> > include, among other things, a reference to the element for the
> > message.
> >
> > In the larger scheme of things, appling this pub/sub structure will
> > have a lot of benefits.  It effectively provides more "seams" on the
> > client-side where logic can be injected, which should lead to less
> > monkey-patching. I also see it as a great mechanism for dealing with
> > Cometd/Websockets, where the server-side can publish events to the
> > client-side.
> >
> > My personal plan is to start introducing the T5 namespace and pub/sub
> > features in 5.3 and go a bit more whole-hog in 5.4.  In fact, the
> > "theme" of 5.4 could be JavaScript: breaking the ties on Prototype,
> > pub/sub, and Websocket support.
> >
> > So, I'm interested in general thoughts on the approach, and also some
> > insight into the memory leak issue.  It would be great if there was a
> > bubbling DOM event that we could use for this purpose. I don't think
> > there is.
> >
> > --
> > Howard M. Lewis Ship
> >
> > Creator of Apache Tapestry
> >
> > The source for Tapestry training, mentoring and support. Contact me to
> > learn how I can get you up and productive in Tapestry fast!
> >
> > (971) 678-5210
> > http://howardlewisship.com
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
> > For additional commands, e-mail: dev-help@tapestry.apache.org
> >
> >
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: dev-help@tapestry.apache.org
>
>

Re: Client-side Pub/Sub vs. Memory Leaks

Posted by Kalle Korhonen <ka...@gmail.com>.
Tying to dom elements sounds reasonable to me. You mean
DOMNodeRemoved? There's such as a thing in the spec but do the
browsers, especially everybody's favorite trouble maker, IE, support
it?  See http://en.wikipedia.org/wiki/DOM_events,
http://stackoverflow.com/questions/3750546/event-observe-domnoderemoved-does-not-work-in-ie
and http://www.bennadel.com/blog/1623-Ask-Ben-Detecting-When-DOM-Elements-Have-Been-Removed-With-jQuery.htm.

Kalle


On Sun, Jun 19, 2011 at 12:03 PM, Howard Lewis Ship <hl...@gmail.com> wrote:
> I've been, effectively, prototyping a client-side pub/sub solution for
> Tapestry with one of my client projects.
>
> The goal is to reduce the abuse of DOM events or other complicated
> structure.  If two components need to work together when they are both
> present in the page, it can be difficult to orchestrate when those
> components render at different times (due to Ajax) or are nested
> within other components (making it hard to obtain a client-side id to
> work with). Having behaviors loosely coupled, using well-known topic
> names, is a major part of the solution for this.
>
> Topics names are of the form "tapestry:zone-updated/myZoneId" or
> "app:master-category-changed".  The convention I'm coming up with is
> that topic names start with a prefix ("tapestry:" for the framework,
> or "app:" for the application), then a name, then optionally a
> client-element id.
>
> Message publication is by topic name, and there's a bubbling system: a
> message published to "foo/bar" can be subscribed to with "foo/bar" or
> "foo".  This will allow, for example, code to be aware of any form
> that's about to submit, or any Zone that's about to update, etc.
>
> The topic message payload (or just "message") is any JavaScript
> object, defined by the code that publishes to the topic.  It is passed
> to any listener functions listening to the topic.
>
> Part of the code is in 5.3.0, part of it is evolving inside my client
> project and will be merged into 5.3.x.
>
> In any case, lots of it is working out nicely. I have some very
> sophisticated logic on the client side and the pubsub system makes
> things possible that otherwise would not be.
>
> However, I do have a concern.  Although its easy for a listener to
> unsubscribe itself, what I'm struggling with is when a topic is
> associated with a DOM element (which is often the case).  I would like
> to have a means of recognizing that a DOM element (or tree of DOM
> elements) has been removed from the DOM, and to automatically destroy
> topic publishers and listeners related to that DOM element.
>
> Failure to do this will likely result in memory leaks inside
> long-running pages, and potentially to client-side exceptions as
> listeners perform updates to DOM elements that are no longer attached
> to the document.
>
> One option I'm considering is to make topic names simple strings, but
> to always associate them with a DOM element.  I.e.:
>
> var publisher = T5.createPublisher("tapestry:form-validate", myForm);
> // createPublisher() returns a publisher function.
> // The return value of the publisher is an array of the return values
> of all listener functions.
>
> T5.sub({ topic: "tapestry:form-validate", element: myForm}, function()
> { ... }); // to match message published for myForm
> // The return value of sub() is a function used to unsubscribe the
> listener function from the topic. This can be used for
> // "listen once" scenarios.
>
> T5.sub("tapestry:form-validate", function() { ... }); // to match
> messages published for any form
>
> var result = publisher({ foo:bar, biff:baz}); // Send the object to
> listener functions
> // Get back an array of return values.
>
> This would replace and formalize the "foo/client-id" pattern I discuss above.
>
> The pub/sub approach above could combine with some code that would
> iterate over elements being removed form the DOM and cleanup the
> publishers/listeners mappings.  Part of this code already exists, to
> clean things up for IE (breaking apart the DOM and JS to prevent
> memory leaks).
>
> As I write this, I'm really liking the idea of always linking topics
> to elements; listener functions could receive two parameters: the
> payload message, and (second & optional) an event object that would
> include, among other things, a reference to the element for the
> message.
>
> In the larger scheme of things, appling this pub/sub structure will
> have a lot of benefits.  It effectively provides more "seams" on the
> client-side where logic can be injected, which should lead to less
> monkey-patching. I also see it as a great mechanism for dealing with
> Cometd/Websockets, where the server-side can publish events to the
> client-side.
>
> My personal plan is to start introducing the T5 namespace and pub/sub
> features in 5.3 and go a bit more whole-hog in 5.4.  In fact, the
> "theme" of 5.4 could be JavaScript: breaking the ties on Prototype,
> pub/sub, and Websocket support.
>
> So, I'm interested in general thoughts on the approach, and also some
> insight into the memory leak issue.  It would be great if there was a
> bubbling DOM event that we could use for this purpose. I don't think
> there is.
>
> --
> Howard M. Lewis Ship
>
> Creator of Apache Tapestry
>
> The source for Tapestry training, mentoring and support. Contact me to
> learn how I can get you up and productive in Tapestry fast!
>
> (971) 678-5210
> http://howardlewisship.com
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: dev-help@tapestry.apache.org
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org