【问题标题】:How do you attach callbacks to the deferred object that is attached to a deferred object?如何将回调附加到附加到延迟对象的延迟对象?
【发布时间】:2011-03-31 20:22:51
【问题描述】:

我有一个支持一系列级联下拉选择列表的 ajax 请求链。当您在第一个下拉列表中选择一个值时,会触发一个请求以填充第二个,当完成(并且下拉填充)时,下一个请求会触发以填充第三个下拉列表,依此类推。

这些请求链的形成方式有一些变化,所以我希望使用 jQuery Deferred 对象来组装请求。

我知道如何将第二个请求链接到第一个请求,但我不知道如何将第三个请求链接到第二个请求。

function Step1() { return $.ajax(<foo>);}
function Step2() { return $.ajax(<foo>);}
function Step3() { return $.ajax(<foo>);}

$(function() {
   Step1().then(Step2).then(Step3);
});

意图是当Step2被解析时触发Step3,但.then(Step2)返回的延迟对象来自Step1,所以将Step3作为回调添加到Step1。

如果你看到this jsFiddle sample,我想我想做什么会更清楚。 编辑Here 是同一个脚本,但在第二次调用中添加了延迟以使其更加明显。

【问题讨论】:

  • 你想要一个queue
  • 我对队列了解不多,我可能过于简化了这个问题。队列如何支持条件执行,例如延迟对象启用完成/失败回调?另外,你可以有多个回调附件吗?那么如果 Step2 成功了就应该执行 Step3 和 4,那么队列是否支持 Step 3 失败而 4 继续执行?
  • 特定队列没有。

标签: jquery jquery-deferred


【解决方案1】:
$(function() {
    $.when(Step1).then(function() {
         $.when(Step2).then(Step3);
    });
});

对于错误处理,我建议您将 Stepn 重写为:

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

使用这种格式的回调可以让你做你想做的事。如果你有超过 5 个步骤,缩进就会变得一团糟,为此建立一个队列可能是值得的。

这是直播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) 将延迟项或延迟项数组添加到队列中。可以与单个延迟项或数组一起使用。选项图如下

{
    "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,一个运行队列中下一项的函数。内部调用,可以手动与延迟属性串联使用。

【讨论】:

  • 如上所示使用.when() 绝对是获得预期结果的一种方式。我在自己的项目中尝试了各种方法,发现只有 .when() 解决方案在 jQuery 2.0 中运行良好
【解决方案2】:

受 James Coglan 的 blog series 启发,我最近开始与这个问题搏斗(请参阅我的问题 here)。

在使用“monads”一段时间后,我回过头来希望可以将延迟对象“链接”在一起。问题是“完成”返回相同的延迟对象,而不是新对象。

我浏览了 jquery 代码一段时间,并认为我无法向 Deferred 或 _Deferred 代码注入任何内容。但是,可以将我们自己的对象作为参数注入promise()函数。所以如果我们创建一个函数来为我们生成一个可链接的承诺......

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());
         }
     };
 }

...然后我们可以用它来拉皮条我们的风格:

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"

有关“之前/之后”代码示例,请参见此处的 jsfiddle:http://jsfiddle.net/Benjol/DjrRD/

【讨论】:

【解决方案3】:

我们很幸运,并且在时间线上有一定的灵活性。我们最终使用了在 jQuery 1.6 中添加到延迟对象的 .pipe() 链接。

感谢大家的帮助!

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多