Slow loading images into an array

I'm using this (sort-of, delayed) recursive function to load images into an array:

this.createNextStim = function(j, i, subarr) {
    this.caller.emptyCanvas();
    this.caller.showText("Creating stimuli: " + j + ": " + i, 0, 0.5, "white");
    if (i < this.nStim[j] && j < this.nStimCat) {
        if (this.stimType[j] == "img") {
            var img = document.createElement("img");
            img.src = this.stimArrParams[j][i];
            img.width = 200;
            img.height = 200;
            subarr.push(img);
            i++;
            img.addEventListener("load", this.createNextStim.bind(this, j, i, subarr));
        } else if (this.stimType[j] == "string") {
            subarr.push(this.stimArrParams[j][i]);
            i++;
            this.createNextStim(j, i, subarr);
        }
    } else {
        this.stimArr.push(subarr);
        j++;
        if (j >= this.nStimCat) {
            this.startTask();
        } else {
            subarr = [];
            i = 0;
            this.createNextStim(j, i, subarr);
        }
    }
};

The idea is that I can specify an array of filenames, this.stimArrParams[j][i], which contains this.nStimCat categories (indexed by j) with this.nStim[j] stimuli (indexed by i) per category. The function runs over the stimuli and categories, creates an image with the respective filename, and when the image is done loading it calls the function for the next stimulus.

It works but it's only loading something like 2 or 3 images per second. The stimuli are JPGs in the 50 - 100 KB range. That seems slower than it should be? Is there maybe something horrendously inefficient in the code? (I'm pretty sure that the stimulus-presentation functions, emptyCanvas and showText, aren't where the delay is.)

Posted Sep 13, 2016, 8:15 PM UTC · edited

14
GeneralYouri•

I'd guess that you're just underestimating the extra time added due to requesting the images one by one, waiting for the previous one to be fully loaded before even sending a request for the next one. I feel like you're trying to forcefully avoid asynchronous code, while this problem is the perfect case for using asynchronous methods. You don't have to wait before an image is fully loaded - you can fire off the requests for all the images you want loaded, all at once. When the requests finish, then you can decide what to do with them.

2
waterlessOP•

Aah, thank you! I'll try it that way.

1
inu-no-policemen•

and when the image is [eventually] done loading it calls the function for the next stimulus.

Yea, don't do that. Request all the images at once. The browser will take care of the queuing.

Use promises. Put them all into an array and then use Promise.all to await their completion.

let paths = ['a.svg', 'b.svg', 'c.svg'];
let promises = paths.map(path => new Promise((resolve, reject) => {
    let image = new Image();
    image.onload = () => resolve(image);
    image.onerror = e => reject(e.message);
    image.src = path;
}));
Promise.all(promises)
    .then(images => console.log(images))
    .catch(console.error.bind(console));
2
waterlessOP•

Thanks very much! I'll look into promises, hadn't used that yet.

1