1

I am having a strange issue here I hope you all can help with.

Project Details

I am working on a simple pub/sub implementation for a larger application that includes a pubsub.subscribe_once() method. This method enables the creation of one-off subscriptions, meaning that a generic subscription is created, and then once the correct "publish" event fires and the subscription callback is run, the subscription deletes itself.

subscribe_once: function(topic, func) {
    var sub = pubsub.subscribe(topic, func),
        old_func = sub.func;

    // rewrite our subscription's method to remove itself after invocation
    sub.func = function() {
        // call the original function
        old_func.apply(this);
        // remove subscription from topic
        pubsub.unsubscribe(sub);
    };

    return sub;
}

Problem

I seem to be having some kind of issue with the memory flow of this process. (In order to best understand the following explanation I suggest you walk through the jsfiddle demo below as I go.) I create a subscribe_once('someevent') subscription, and then fire publish('someevent'). What you would expect to see when the publish method is invoked is that the topics hashtable contains a "someevent" key, which references an array of Subscription objects. And in fact, if you reference topics["someevent"], you see an array with a single Subscription. If, however, you reference topics you see the "someevent" key, but the array is empty!

By commenting out pubsub.unsubscribe(sub); the problem is eliminated, even though this function does not appear to be fired until after we run console.log(topics).

Further, this does not seem to be an issue with the way a given browser "threads" console.log; try console.log(topics, topics[topic], topics, topics[topic]) and you get the same result.

Demo: http://jsfiddle.net/4Ab6c/

Any help would be greatly appreciated! Thanks.

Squirkle
  • 933
  • 1
  • 7
  • 22

1 Answers1

2

I'm still looking for some documentation to back me up, but I suspect the object display in the console is performing lazy evaluation on your topics object. I added console.log(topics) to the subscribe method after sub is pushed onto the array and I get the same result as your 'but not here' log. When I wrap the final line of your fiddle pubsub.publish('someevent') in setTimeout and I get the Object tree open before the publish callback runs, then it shows the subscription in the array and it stays that way even after the callback runs. If I don't open the object tree in the console before the callback runs then I see the empty array.

I will keep searching for a blog post or something that confirms lazy evaluation is occurring.

Just in case I haven't made it obvious enough, by lazy I mean, the console isn't gathering the details of the object until the tree view is clicked open in the console.

I am working in Chrome.

UPDATE

I have found similar behavior on Firefox as well. Firefox recognizes that there is one object in the array but if you don't drill down into the array before the publish even fires then the drill-down on the array will be empty.

I updated the fiddle from your comment:

http://jsfiddle.net/4Ab6c/2/

Please try this:

  • Run the fiddle and expand the object tree for the first console.log before the publish event fires, I set it to a five second timeout but you could make it longer or shorter depending on how quickly you can get down to the console and click the inspector open.

  • You should see the subscribe object in the array as expected.

  • Clear the console and run the fiddle again. This time do not open the object inspector until after the publish event has fired and all the code is done running.

  • Now when you open the object inspector of the first console.log you should not see the subscription event in the array.

UPDATE 2 Here is a much simpler fiddle that exhibits the same behavior:

http://jsfiddle.net/4Ab6c/3/

If you expand first before second shows up then you will get foo: bar. If you expand first after second shows up you will get foo: baz.

UPDATE 3

And, voila, another SO question seconds the motion on lazy evaluation.

Community
  • 1
  • 1
patrickmcgraw
  • 2,465
  • 14
  • 8
  • It would have to be some seriously lazy evaluation: see this update, specifically the setTimeout wrap around "unsubscribe" at the bottom of the doc. http://jsfiddle.net/4Ab6c/1/ – Squirkle Sep 14 '12 at 21:20
  • @Squirkle Even without backing docs I have a fiddle that I think confirms what is going on. See update 2. – patrickmcgraw Sep 14 '12 at 22:03
  • You're absolutely right. I wonder what the value is in the lazy evaluation? It seems that this would just make it difficult to track changes to an object. See the following for a proof: http://jsfiddle.net/4Ab6c/4/ – Squirkle Sep 14 '12 at 22:25
  • @Squirkle I totally agree. This actually bit me on a backbone app earlier this week although I didn't realize this was the issue until researching your question. One tactic mentioned on that other SO question is to wrap the object in `JSON.stringify` which causes the object to be evaluated as log is run. – patrickmcgraw Sep 14 '12 at 22:35