André
André

Reputation: 25554

Javascript - Async functions causing incorrect order

I'm building a photos uploader in Javascript and to read the already uploaded photos I've this code:

    // Populate with existing photos
    var index;
    for (index = 0; index < params.photosToPopulate.length; ++index) {

        // Get the data of the image
        getDataUri(params.photosToPopulate[index], params, function(dataURI){
            // Get the Blob
            dataURItoBlob(dataURI, function(dataurl){

                var blob = URL.createObjectURL(dataurl);

                // fileItem
                var fileItem = document.createElement("div");
                fileItem.setAttribute('angle', 0);
                fileItem.setAttribute('blob', blob);
                fileItem.className = 'imageloaderplusFile';
                filesdiv.appendChild(fileItem);

                // fileImg
                var fileImg = document.createElement("div");
                fileImg.className = 'imageloaderplusImage';
                fileImg.style.backgroundImage = 'url(' + dataURI + ')';
                fileItem.appendChild(fileImg);

                // fileRotate
                var fileRotate = document.createElement("div");
                fileRotate.className = 'imageloaderRotate';
                fileRotate.setAttribute('title', 'Rotate');
                fileItem.appendChild(fileRotate);
                fileRotate.onclick = function(){

                    var angle;

                    switch(parseInt(this.parentNode.getAttribute('angle'))){
                        case 0:
                            angle = 90;
                            break;  
                        case 90:
                            angle = 180;
                            break;
                        case 180:
                            angle = 270;
                            break;
                        case 270:
                            angle = 0;
                            break;
                    }

                    this.parentNode.setAttribute('angle', angle);

                    // css
                    var image = this.parentNode.firstChild;
                    image.style.webkitTransform = 'rotate(' + angle + 'deg)'; 
                    image.style.mozTransform = 'rotate(' + angle + 'deg)'; 
                    image.style.msTransform = 'rotate(' + angle + 'deg)';
                    image.style.oTransform = 'rotate(' + angle + 'deg)';
                    image.style.transform = 'rotate(' + angle + 'deg)';

                    // draw
                    ImageLoaderPlus.draw(this.parentNode, params);
                }

                // fileRemove
                var fileRemove = document.createElement("div");
                fileRemove.className = 'imageloaderRemove';
                fileRemove.setAttribute('title', 'Remove');
                fileItem.appendChild(fileRemove);
                fileRemove.onclick = function(){
                    this.parentNode.parentNode.removeChild(this.parentNode);
                }

                // draw
                ImageLoaderPlus.draw(fileItem, params); 

            });
        });

    } // End For Loop

In the For Loop I've the ordered photos, but some times because of the async functions getDataUri() and dataURItoBlob() the order of the items in the For Loop will not translate to the order in de UI/DOM and the photos are displayed in the incorrect order. How can I prevent this from happening?

The async functions are this ones:

    function getDataUri(url, params, callback) {

        var image = new Image();
        var canvas = document.createElement("canvas");
        var ctx = canvas.getContext("2d");
        //image.crossOrigin = "anonymous";  // This enables CORS

        // get scale
        var scale = (params.resize && (image.width > params.maxWidth || image.height > params.maxHeight)) ? Math.min(params.maxWidth / image.width, params.maxHeight / image.height) : 1;

        image.onload = function (event) {
            try {
                canvas.width = Math.round(image.width * scale);
                canvas.height = Math.round(image.height * scale);
                ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
                //console.log( ctx.canvas.toDataURL('image/jpeg', params.jpegQuality) );
                result = ctx.canvas.toDataURL('image/jpeg', params.jpegQuality)
                callback(result);
            } catch (e) {
                alert(e);
            }

        };
        image.src = url;
    }       

    // To convert URL to Blob
    function dataURItoBlob(dataURI, callback) {
            // convert base64 to raw binary data held in a string
            // doesn't handle URLEncoded DataURIs

            var byteString;
            if (dataURI.split(',')[0].indexOf('base64') >= 0) {
                byteString = atob(dataURI.split(',')[1]);
            } else {
                byteString = unescape(dataURI.split(',')[1]);
            }

            // separate out the mime component
            var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

            // write the bytes of the string to an ArrayBuffer
            var ab = new ArrayBuffer(byteString.length);
            var ia = new Uint8Array(ab);
            for (var i = 0; i < byteString.length; i++) {
                ia[i] = byteString.charCodeAt(i);
            }


            try {
                result = new Blob([ab], {type: mimeString});
                //return new Blob([ab], {type: mimeString});
            } catch (e) {
                // The BlobBuilder API has been deprecated in favour of Blob, but older
                // browsers don't know about the Blob constructor
                // IE10 also supports BlobBuilder, but since the `Blob` constructor
                //  also works, there's no need to add `MSBlobBuilder`.
                var BlobBuilder = window.WebKitBlobBuilder || window.MozBlobBuilder;
                var bb = new BlobBuilder();
                bb.append(ab);
                result = bb.getBlob(mimeString);
                //return bb.getBlob(mimeString);
            }

            callback(result);       
    }

Any clues on how to avoid this problem?

Best Regards,

Upvotes: 0

Views: 72

Answers (1)

trincot
trincot

Reputation: 350272

You could solve this by storing the dataurl values in an array as you receive them, but at the right index in that array. And then when you have counted that you received all those dataurl values, you perform the loop over that array and execute the element creation code like you have it now.

// Intermediate array to store the dataURI and dataurl values in order
var urls = [];
var URIs = [];
// Keep count of how many dataurl values we received
var leftOver = params.photosToPopulate.length;
// Iterate in a way that you create a new closure on every iteration, 
// each iteration has its proper index variable
params.photosToPopulate.forEach(function (photo, index) {
    // Get the data of the image
    getDataUri(photo, params, function(dataURI){
        // Store at the right place in our array
        URIs[index] = dataURI;
        // Get the Blob
        dataURItoBlob(dataURI, function(dataurl){
            // Only store the url at the right place in our array
            urls[index] = dataurl;
            // ... and update the count
            leftOver--;
            // All done? Then iterate over the dataurl values in the correct order
            if (!leftOver) urls.forEach(function(dataurl, index) {
                var dataURI = URIs[index]; // get correct dataURI
                var blob = URL.createObjectURL(dataurl);
                // fileItem
                var fileItem = document.createElement("div");
                // ...etc, all your element creation code comes here, unchanged. 
                // ...

                // draw
                ImageLoaderPlus.draw(fileItem, params); 
            });
        });
    });
});

Upvotes: 1

Related Questions