Search code examples
javascriptjqueryajaxjquery-deferred

How to line up jQuery deferred objects?


I need to delay every Ajax call until the previous function (hashcode.Sign()) has been completed. Unfortunately my code doesn't wait until hashcode.Sign() is completed and that messes everything up, since that function creates a new session and it cannot be overwritten in order to things to work. How should I do that? I'm new to Deferred objects so pardon my lack of knowledge.

    for (var l = 0; l < fileArray.length; l++) {
        var d = new jQuery.Deferred();
        d.resolve(openDdoc(fileArray[l].url, $pid, fileArray[l].id));
        d.done(function() { console.log('done'); });
    }

    function openDdoc(url, pid, fid) {
        var def = new jQuery.Deferred();
        $.when(def).done(function() { console.log('Function completed!'); });
        def.resolve($.ajax({
                    url: '/open-ddoc',
                    type: 'post',
                    async: false,
                    data: {doc: url, pid: pid, fid: fid},
                }).done(function (data) {
                    hashcode.Sign();
                }).fail(function(data) {
                    console.log('this shit failed');
                }));
    }

The hashcode.Sign() function includes some more Ajax calls and calls to other functions. I can include it later if neede. I know I could resolve all of this with setTimeout(), but that wouldn't be my first choice.


Solution

  • It's not immediately obvious but the sequentiality you seek can be expressed in the form of fileArray().reduce(...).

    This tried and tested pattern is outlined here under the subheading "The Collection Kerfuffle". (The whole article is well worth reading).

    fileArray.reduce(function(promise, file) {
        return promise.then(function() {
            return $.ajax({
                url: '/open-ddoc',
                type: 'post',
                async: true, // <<<<< always TRUE!!
                data: {doc: file.url, pid: $pid, fid: file.id}
            });
        }).then(function(data) {
            console.log(file.id + ': done');
            return hashcode.Sign(); // hashcode.Sign() must return a promise
        });
    }, $.when()) // <<<<< resolved promise to get the chain started
    .then(function() {
        console.log('all done');
    });
    

    In this pattern, each member of fileArray is visited in turn (synchronously), adding a fresh .then() to a growing promise chain at each visit.

    The resolved starter promise, $.when(), causes the constructed chain to start settling, and the ajax calls are made in sequence.

    The ajax call doesn't need to be in a separate function, unless it's going to be called elsewhere.

    It should be emphasised that hashcode.Sign() (which we understand to be asynchronous) must be written in the right way such that it returns a valid promise. If not, then the settlement process has no way of ensuring its asynchronism(s) to be complete before proceeding.