Question

I am currently trying to build a File Uploader using the HTML5 FileAPI. The File Uploader is supposed to handle multiple files and show image previews if the file is an image.

since the FileReader class works asynchronously I want to wait until all the the files have been read. Therefore I am using Deferreds.

A method which reads the file is returning a promise. Another method loops through all files and pushes all promises into an array. Then I'm applying the then() method once all promises have been added to my array.

Now to my problem. Since the then() method is only called once, when I got all promises. I have no chance to process every single promise. What I want to do is, to loop through all my promises once they are all there and return the result.

This is my FileProcessor Object

read: function(file) {
    var reader = new FileReader();
    var deferred = $.Deferred();

    reader.onload = function(event){
        deferred.resolve(event.target.result);
    };

    reader.onerror = function() {
        deferred.reject(this);
    }

    if(/^image/.test(file.type))
        reader.readAsDataURL(file);

    return deferred.promise();
},

And here comes the FileManager Object's handleFileSelect() method that is supposed to call the FileProcessor.read() method.

handleFileSelect: function(e){
    var $fileList = $('#file-list');

    var files = e.target.files;
    var promises = []; //Promises are going to be stored here
    var filestring = '';

    var processedFile;

    // Loop trough all selected files
    for(var i = 0; i < files.length; i++){
        // Store the promise in a var...
        processedFile = FileProcessor.read(files[i]);   
        // And push it into the array where the promises are stored
        promises.push(processedFile);                   
    }

    // Once all deferreds have been fired...
    $.when.apply(window, promises).then(function(){
        for(var i = 0; i < promises.length; i++){
            // PROBLEM HERE: I need to get the 
            // result from my resolved Deferred
                            // of every single promise object
            console.log(promises[i]);
        }
    });

},

Am I using the wrong approach for deferreds and promises? Aren't they supposed to be used like I'm trying to do and is there a more elegant way to achieve my purpose?

Was it helpful?

Solution

Am I using the wrong approach for deferreds and promises?

Yes. In the asynchronous callback you should not access promises, but only the callback arguments. The return promise of $.when does resolve with an array of the .resolve() parameters for each of the promises in the array - you can loop over the arguments object to access the results:

$.when.apply($, promises).then(function () {
    for (var i = 0; i < arguments.length; i++) {
        var singleresult = arguments[i][0];
        console.log(singleresult);
    }
});

OTHER TIPS

you can use the arguments object to refer all the values returned by the promise objects

$.when.apply($, promises).then(function () {
    for (var i = 0; i < arguments.length; i++) {
        var data = arguments[i][0];
        // PROBLEM HERE: I need to get the 
        // result from my resolved Deferred
        // of every single promise object
        console.log(data);
    }
});

I see you heve accepted an answer but you might like to be aware that your handleFileSelect method can be significantly simplified as follows

handleFileSelect: function(e) {
    var promises = $.map(e.target.files, function(file) {
        return FileProcessor.read(file);
    });
    $.when.apply(window, promises).then(function() {
        $.each(arguments, function(i, a) {
            console.log(a[0]);
        });
    });
},

Of course it will get more complicated again when you flesh out the result handling but this should give you a nice concise basis.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top