Reputation: 281
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
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
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
Reputation: 3298
I think you should take a look at locks in javascript:
https://github.com/Wizcorp/locks
Upvotes: 2