ondrejba
ondrejba

Reputation: 281

How to synchronize asynchronous methods in javascript?

I have an image processing javascript. Through a file input with an id 'image_input' user can choose to upload an unlimited number of images. When an user uploads images, my jQuery code below catches them and calls my image processing code for each image.

$('#image_input').change(function() {
    var input = $(this);
    image_count = input[0].files.length;
    for (var i = 0; i < image_count; i++) {
        Uploader.processImage(input[0].files[i]);
    }
});

The process image function creates a new HTML img element and loads the user uploaded image into it. The script waits until the image is loaded and then it converts it into a suitable format and size. Finally, the image is saved into an array: Uploader.images.

Uploader.processImage = function(image) {
    var image_element = document.createElement('img');
    var image_url = window.URL.createObjectURL(image);
    image_element.onload = function() {
        image_data = convert(this);
        Uploader.images.push(dataUriToBlob(image_data));
    }
    image_element.src = image_url;
}

I want the images to be stored in the array in the order they were uploaded. The problem is that the onload function is asynchronous, which means that I cannot control in which order the images are saved. For instance, I choose to upload 3 images. Image 1 and image 2 are each 10MB of size but image 3 is only 300 KB. Therefore, conversion of the image 3 is finished first and the final order of the images in the array is 3, 1, 2 (or 3, 2, 1).

How can I synchronize the execution of processImage so that the next image is processed only when the conversion of the previous one is finished?

Upvotes: 0

Views: 81

Answers (3)

llernestal
llernestal

Reputation: 566

I would use Promises to solve a problem like this. I like the Q library that implements the Promises/A+ standard.

More on Promises on HTML5 Rocks

Made a small image loading script using the Q and Qimage libraries showing the principles of Promises in a situation similar to the one in the question:

var arrOfImgUrls = [url1, url2, url3];    

//Loads images from an array of URLs
function loadImages(urls) {
    var promises = [];
    for (var i = 0; i < urls.length; i++) {
        //Qimage loads and image from an URL and returns a Promise.
        //If the Promise is fullfilled we get an <img> node
        //as the resolve value.
        //
        //We push the Promises on to an array
        promises.push(Qimage(urls[i]))
    }
    //Return the array of Promises
    return promises;
}

//Q.all() returns a promise that is fulfilled with an array
//containing the fulfillment value of each promise, or is rejected 
//with the same rejection reason as the first promise to be rejected.
Q.all(loadImages(arrOfImgUrls))
//If all Promises are fullfilled the the resolve callback is called
.then(function (imgs) {
    //We get an array of <img> nodes in the same order as the
    //'arrOfImgUrls'-array
    console.log(imgs);
},
//Else the reject callback is called with the same rejection 
//reason as the first promise to be rejected.
function (error) {
    console.log(error);
});

Upvotes: 1

Goodword
Goodword

Reputation: 1645

Locks are great, as @Esse answered. I also recommend checking out Fibers (This is what I usually use) and Promises.js.

This is a great article about Async in the Meteor framework. It gives a great overview of how Fibers work though.

Upvotes: 1

Esse
Esse

Reputation: 3298

I think you should take a look at locks in javascript:

https://github.com/Wizcorp/locks

Upvotes: 2

Related Questions