0

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;
            });
    });
francesca
  • 559
  • 3
  • 10
  • 19
  • Possible duplicate of [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – JJJ Sep 11 '16 at 17:47
  • So, can you not use the second approach then? – Endless Sep 11 '16 at 17:56
  • @Endless I need behaviour like in a second case. Am I right, that when I save image as a variable - it keeps this value until it will be used in promise? And why the model doesn't keep this value? – francesca Sep 11 '16 at 18:07
  • Yes, if you assign a variable it keeps it when you use it in the promise. Your problem is related to what @juhana mention but yours are a little bit different – Endless Sep 11 '16 at 18:14

1 Answers1

1

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.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143