Letokteren
Letokteren

Reputation: 809

Wait for image loading to complete in JavaScript

I'm loading images with JavaScript. Something like this:

images[0]=new Image();
images[0].onload=function(){loaded++;console.log(loaded)};
images[0].src="assets/img/image.png";

When I look at the log, I see that all the images are loaded nicely, since the value of the "loaded" variable increases with each loaded image.

However I would like to stop any further action to be executed until this amount reaches it's maximum, so right after setting up the images, I place a while cycle.

while(loaded<11){
    document.getElementById("test").innerHTML="Loading "+loaded+"/11";
    console.log(loaded);
}
//Some code here which should only run after everything has been loaded
//In other words: when the statement in the while cycle becomes false

However my browser simply crashes, since the while seems to be stuck in an infinite loop. When I check the log, I see that "0" was written 1000 times, and after that, the numbers from 1 to 11 (which implies that the images in fact gets loaded, but the while does not care about it, and crashes faster than it could happen).

I believe that the method I'm trying to use here is not the right approach to solve this problem.

How can I put everything on hold until every asset which is needed for the site is loaded?

Upvotes: 6

Views: 29686

Answers (3)

Kimbatt
Kimbatt

Reputation: 341

Using promises and async functions, there is a nice way to wait until all the images are loaded (no callbacks, no loaded image counting):

async function loadImages(imageUrlArray) {
    const promiseArray = []; // create an array for promises
    const imageArray = []; // array for the images

    for (let imageUrl of imageUrlArray) {

        promiseArray.push(new Promise(resolve => {

            const img = new Image();
            // if you don't need to do anything when the image loads,
            // then you can just write img.onload = resolve;

            img.onload = function() {
                // do stuff with the image if necessary

                // resolve the promise, indicating that the image has been loaded
                resolve();
            };

            img.src = imageUrl;
            imageArray.push(img);
        }));
    }

    await Promise.all(promiseArray); // wait for all the images to be loaded
    console.log("all images loaded");
    return imageArray;
}

Or you can wait for a single image to load:

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

    await imageLoadPromise;
    console.log("image loaded");
    return img;
}

You can use it like this (using promise chaining):

loadImages(myImages).then(images => {
    // the loaded images are in the images array
})

Or inside an async function:

const images = await loadImages(myImages);

Upvotes: 24

Said Hasanein
Said Hasanein

Reputation: 320

Personally I hate using while()... I think the easiest way to do it, is using event listeners.

var img = new Image;
img.addEventListener("load", function () {

//Img loaded

});
img.src= e.target.result;

Upvotes: 8

Blindman67
Blindman67

Reputation: 54041

Javascript is single threaded. This means that if you add an event listener that listener will not run until the current execution has completed. Hence if you start a loop that relies on a event to end it it will never happen as the event will never fire because the current execution is preventing it from running. Also the events are place on the call stack asynchronously, thus if your execution is slower than the rate of event firing (placing a call on the call stack) you also risk the a page crash. This is a common mistake when using setInterval when the interval is set to less time than the code takes to execute. NEVER USE setInterval.

Just remember Javascript can not do two things at once.

The best way to deal with resource monitored loading is to use setTimeout.

var allLoaded = false;
var imgCount = 0;  // this counts the loaded images
// list of images to load
const imageURLS =["a.jpg","b.jpg","c.jpg","d.jpg","e.jpg"];
// array of images
var images = [];

const onImageLoad = function(){ imgCount += 1; } // onload event
// loads an image an puts it on the image array
const loadImage = function(url){
    images.push(new Image());
    images[images.length-1].src = url
    images[images.length-1].onload = onImageLoad;
}
const waitForLoaded = function(){
    if(imgCount === images.length){
        allLoaded = true;   // flag that the image have loaded
    }else{
        // display  the progress here
        ...

        setTimeout(waitForLoaded,100); // try again in 100ms
    }
}

// create the images and set the URLS
imageURLS.forEach(loadImage);

setTimeout(waitForLoaded,100);  // monitor the image loading

Upvotes: 3

Related Questions