意外的延迟对象的行为

Unexpected deferred object's behaviour

任何人都可以澄清一下下面的代码有什么问题吗(已经阅读了很多文档和示例,但仍然不知道发生了什么)

function t() {
    var d = $.Deferred();
    setTimeout(function(){
        d.resolve();
    }, 5000);
    return d.promise();
}
function test() {
    var dd = $.Deferred();
    $.ajax("/echo/json/").done(function() {
        dd = t();
        dd.done(function() { alert(" dd.done inside ajax")});
    });
    dd.done(function() { alert(" dd.done outside ajax")});
}
test();

输出是(在 ~ 5 秒内):

"dd.done inside ajax:"

为什么第二个 .done 不起作用?

因为那个延迟的对象没有被解析。您正在创建 2 个延迟对象并解析其中一个。

我们来看看test():

function test() {
    var dd = $.Deferred();
    $.ajax("/echo/json/").done(function() {
        dd = t();
        dd.done(function() { alert(" dd.done inside ajax")});
    });
    dd.done(function() { alert(" dd.done outside ajax")});
}
test();

局部变量dd被初始化为一个新的jQuery延迟对象。然后,启动一个 ajax 操作,并给出一个 "done" 回调,它将调用另一个测试函数 t().

$.ajax() 调用基本上会立即 return,远早于它的 .done() 回调 运行。紧接着,另一个 .done() 回调为在函数开始时创建的 Deferred 实例建立。

现在,当 ajax "done" 回调 运行 时, dd 的值——最初创建的 Deferred 对象——将是 用 return 来自 t() 的 Promise 覆盖。最终 .done() 回调将是 运行,但没有任何东西解析第一个 Deferred 实例,因此 "outside" 回调永远不会发生。

您的第二个警报从未被调用,因为您分配给变量 dd 的原始延迟从未被解析,因此它的 .done() 处理程序从未被调用。

您在此处创建一个延迟并将其分配给 dd

var dd = $.Deferred();

然后,您设置了一个 .done() 处理程序:

dd.done(function() { alert(" dd.done outside ajax")});

但是,当您的 ajax 函数完成时,您可以使用以下行将不同的承诺分配给变量 dd

dd = t();

并且,因此没有任何东西解决原始承诺,因此它的 .done() 处理程序永远不会被调用。


我建议改用这种设计:

function t() {
    var d = $.Deferred();
    setTimeout(function(){
        d.resolve();
    }, 5000);
    return d.promise();
}
function test() {
    return $.ajax("/echo/json/").then(function() {
        console.log("ajax call done");
        return t();
    }).then(function() {
        console.log("after timer");
    });
}

test().then(function() {
    console.log("everything done");
});

工作演示:http://jsfiddle.net/jfriend00/atafc5hj/

这说明了以下有用的概念:

  1. 使用已经从 $.ajax() 返回的承诺,而不是创建自己的承诺。
  2. 将另一个 activity 链接到该承诺。
  3. .then() 处理程序返回另一个承诺,使序列也等待该承诺。
  4. 正在从 main 函数返回链式承诺,这样您就可以看到一切何时完成。