Search code examples
javascriptjqueryjquery-deferredjquery-file-upload

Deferreds in a piece of real code


I have some idea about Deferreds and what they do but I can't understand their usage in a piece of code I am working with right now. This code is from jquery-file-upload plugin and file jquery-fileupload-ui.js:

stop: function (e) {
    var that = $(this).data('blueimp-fileupload') ||
            $(this).data('fileupload'),
        deferred = that._addFinishedDeferreds();
    $.when.apply($, that._getFinishedDeferreds())
        .done(function () {
            that._trigger('stopped', e);
        });
    that._transition($(this).find('.fileupload-progress')).done(
        function () {
            $(this).find('.progress')
                .attr('aria-valuenow', '0')
                .children().first().css('width', '0%');
            $(this).find('.progress-extended').html(' ');
            deferred.resolve();
        }
    );
},

_addFinishedDeferreds: function (deferred) {
    if (!deferred) {
        deferred = $.Deferred();
    }
    this._finishedUploads.push(deferred);
    return deferred;
},

_getFinishedDeferreds: function () {
    return this._finishedUploads;
},

In the stop method, there are two constructs that I don't understand:

1)

$.when.apply($, that._getFinishedDeferreds())
.done(function () {
    that._trigger('stopped', e);
});

2)

deferred = that._addFinishedDeferreds();
//and later in the _transition function
deferred.resolve();

The second construct repeats throughout the whole code. I can see that _addFinishedDeferreds creates a deferred (if it is not passed as an argument), adds it to _finishedUploads and that deferred is later resolved. But I am missing the meaning of all this. What is it good for? Why the code can't work without it? And I don't understand the meaning of the first construct at all. Anyone can shed some light on this?


Solution

  • First I assume you understand basic usage of Deferreds, including resolve() and $.when().

    From my understand of this code snippet, the stop function will be called multiple times, each time a new Deferred is created and will not be resolved until the transition is completed, though I don't know what transition it is because you did not show us its code. The Deferred object here, is to signal the completion of transition, and then a stopped event is triggered.

    Details

    1)

    $.when.apply($, that._getFinishedDeferreds())
    .done(function () {
        that._trigger('stopped', e);
    });
    

    $.when.apply($, that._getFinishedDeferreds()) generates a new Deferred which is resolved after every Deferred is resolved in the array returned by _getFinishedDeferreds(). Then the callback function in done() triggers the stopped event.

    2)

    deferred = that._addFinishedDeferreds();
    //and later in the _transition function
    deferred.resolve();
    

    Your understanding is correct. The intention of this code (if I guess correctly) is to make sure the event is not triggered until all transition is finished.

    However if so there's a bug that the event trigger callback:

    function () {
        that._trigger('stopped', e);
    }
    

    is attached each time stop() is called so the event may be triggered multiple times.