0

I am fairly new to javascript. Let's say I have the following code.

let sources =["http://localhost:3001/resources/black_bish",""http://localhost:3001/resources/white_bish""]
let loaded_images=0
sources.forEach(ele = > {
    let img = new Image()
    img.src = ele
    img.onload = function () {
        loaded_images=++
    }
})

Here I had a question about Javascript and concurrency. Can't the 2 callbacks be called at the same time similar to when working with threads? Here, won't there be a race condition? If I were to perform a different action than "loaded_images=++", is there any race condition like manipulating a data structure that I should be worried about?

Thank You

  • Let's say that you have an image of just a few kilobytes, and one that is at least 1MB. No matter the order of execution the larger image will take longer to download and therefor the callback will executed later than the smaller image. So yes, you could have a race condition for you cannot know the order of images loaded. There are methods to help you do something after all of the images have been loaded, like Promises. – Emiel Zuurbier Mar 14 '20 at 15:38
  • @EmielZuurbier Thank You. I was looking up some more about promises. Is this similar to what you mention https://stackoverflow.com/a/11072533/12816052 ? – Srijan Joshi Mar 14 '20 at 15:54
  • It is kind of similar in terms of how it works. But Promises are native now. You should read up on the docs. Later I'll give you an eample – Emiel Zuurbier Mar 14 '20 at 16:32

1 Answers1

0

One way is to return a Promise for each image that you load. This promise will resolve, or in laymen terms: continue when the right condition is met, whenever the images has been loaded. It's like a return statement but instead of ending the function you continue to the next step.

Promise.all is a method on the promise constructor which takes in an array of promises. When all of the promises in the array have been fullfilled (meaning resolve has been called) then do something with the values of all of the promises.

const loadImage = (source) => new Promise(resolve => {
  let img = new Image();
  img.onload = function() {
    resolve(img);
  };
  img.src = source;
});

const whenAllImagesAreLoaded = (...sources) => Promise.all(
  sources.map(source => loadImage(source));
);

whenAllImagesAreLoaded('image-1.jpg', 'image-2.jpg').then((images) => {
  images.forEach(image => {
    console.log(image, ' is loaded');
  });
});

Another example is also with promises in combination with the async / await syntax, which halt execution until the promise you are waiting for has been fulfilled.

This opens up the possibility to, for example: load images one after another, after the previous image has been loaded.

async function loadImagesSequentially(...sources) {
  for (const source of sources) {
    let image = await loadImage(source);
    console.log(image, ' is loaded');
  }
}

Both methods give you better control over how to handle race conditions, or eliminate them completely. I'd suggest that you practice as much as you can with promises. It is a very powerful tool in your JavaScript toolbox.

If you have any questions, please let me know.

Emiel Zuurbier
  • 19,095
  • 3
  • 17
  • 32
  • Whoah! Thanks a lot, man. This works. I couldn't find the exact answer I was looking for when searching online. Your example perfectly illustrated a clean use case of promises and it was exactly what I was looking for. – Srijan Joshi Mar 14 '20 at 17:51