francesca francesca - 2 months ago 8
Javascript Question

Javascript closure: difference between accessing the model directly and using a variable

I've noticed that this code does not work as I expected.

Message RENDER_IMAGE is broadcasted multiple times with different image in payload, but self.model.get("image") in code below always returns last image. Why?

bus.subscribe("RENDER_IMAGE", (message, payload) => {
const self = this;
self.model.set(payload);

self.render().then(function() {
bus.broadcast("IMAGE_RENDER_COMPLETE", self.model.get("image"));
});
});


Meanwhile this code works correct:

bus.subscribe("RENDER_IMAGE", (message, payload) => {
const self = this;
self.model.set(payload);
const image = self.model.get("image"));

self.render().then(function() {
bus.broadcast("IMAGE_RENDER_COMPLETE", image;
});
});

Answer

In the first example you are retrieving the image field from the model after the render is completed.
In the meantime, one or more RENDER_IMAGE events could have been fired before the first render was complete. Every time such an event fires, you are updating the image field.

I.e. something like this happens

RENDER_IMAGE triggered
Set image field            (image = 0)
Start render (0)

RENDER_IMAGE triggered
Set image field            (image = 1)
Start render (1)

Render finished (0)
Get image field (1, because image = 1)

Render finished (1)
Get image field (1, because image = 1)

In the second example your are retrieving the image filed right after you set it. Since JavaScript is single threaded it is not possible that another self.model.set(payload); was called before const image = self.model.get("image")); is executed:

RENDER_IMAGE triggered
Set image field            (image = 0)
Get image field (0, because image = 0)
Start render (0)

RENDER_IMAGE triggered
Set image field            (image = 1)
Get image field (1, because image = 1)
Start render (1)

Render finished (0)

Render finished (1)

If you still wonder why there is a difference between accessing the model directly and using a variable instead: Every invocation of the event handler has it's own image variable, but they all access the same, shared model.

That's one of the characteristics of shared data, which is why shared data has to be handled carefully when dealing with asynchronous processes.