How do you attach callbacks to the deferred object that is attached to a deferred object?

对着背影说爱祢 提交于 2019-12-06 07:04:48
$(function() {
    $.when(Step1).then(function() {
         $.when(Step2).then(Step3);
    });
});

For error handling I recommend you rewrite Stepn to :

function Stepn() { 
    return $.ajax(<foo>).fail(function() {
         // handle failure
    });
}

Using callbacks in this format allows you to do what you want. if you have more then 5 steps the indenting becomes a mess and it might be worthwhile to build a queue for this.

Here's a live example

var Queue = function() {
    var q = [];
    var that = this;

    // If items in queue then run them.
    function moveNext() {
        if (q.length > 0) {
            that.runItem();
        }
    }

    // run first item in queue
    this.runItem = function() {
        // get item
        var item = q.shift();
        // when deferred object then run then ...
        $.when(item.item).then([item.options.done, function() {
            // item finished, move to next.
            moveNext();
        }], [item.options.fail, function() {
            // if run item always then move next on failure.
            if (item.options.always) {
                moveNext();
            }
        }]);
    };

    this.add = function(def, options) {
        // if array then call add on each item in array
        if ($.isArray(def)) {
            for (var d in def) {
                this.add(d, options);
            }
            // return as we are done.
            return this;
        }
        // push item onto array
        q.push({
            item: def,
            options: options
        });
        // if items & not delay then run item.
        if (q.length === 1 && !options.delay) {
            this.runItem();
        }
        // enable jQuery style chaining \o/
        return this;
    };
};

Queue.add([def, def, ...], options) Adds a defferred item or an array of deferred items to the queue. Can be used with either a single deferred item or an array. The options map is as follows

{
    "delay" : Boolean, // if true do not run the item in the queue after appending it.
    "done" : Function, // optional done call back
    "fail" : Function, // optional fail call back
    "always": Boolean // if true run the next item in the queue even if this item fails.
}

Queue.runItem, a function that runs the next item in the queue. Called internally, possible to be used manually in concatenation with the delay property.

Benjol

I started wrestling with this recently (see my question here), inspired by a blog series by James Coglan.

After messing around with 'monads' for a while, I came back to wishing that it were possible to 'chain' deferred objects together. The problem is that "done" returns the same deferred object, instead of a new one.

I browsed the jquery code for a while, and figured there was no way I could inject anything into the Deferred or _Deferred code. However, it is possible to inject our own object as a parameter to the promise() function. So if we create a function which will generate a chainable promise for us...

var Chainable = function Chainable() {
     return {
         chain : function(next) { //next: another function which returns Deferred
                 var newDef = $.Deferred(); //we resolve this when next is done
                 //next line: call next with (a||null) for method-tolerance
                 this.done(function(a) { next(a||null).done(newDef.resolve); });
                 return newDef.promise(Chainable());
         }
     };
 }

...we can then use it to pimp our style:

var asyncMessage = function(msg) {
    var dfd = new jQuery.Deferred();
    setTimeout(function() { dfd.resolve(msg); }, 1000);
    return dfd.promise(Chainable());
};

asyncMessage("Chained:A")
     .chain(function(m) { return asyncMessage(m + "B"); })
     .chain(function(m) { return asyncMessage(m + "C"); })
     .done(log); // -> outputs "ABC"

See the jsfiddle here for the 'before/after' code samples: http://jsfiddle.net/Benjol/DjrRD/

We were fortunate and had some flexibility in the timeline. We ended up using the .pipe() chaining added to deferred objects in jQuery 1.6.

Thanks to everyone for their help!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!