意外的延迟对象的行为

时间:2015-05-30 01:30:57

标签: javascript jquery jquery-deferred

请不要请说明下面的代码有什么问题(已经阅读了很多文档和示例,但仍然不知道发生了什么)

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无效?

3 个答案:

答案 0 :(得分: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 Deferred对象。然后,启动一个ajax操作,并给出一个“完成”回调,它将调用另一个测试函数t()

$.ajax()调用将在其.done()回调运行之前很久就立即返回。在此之后,为在函数开头创建的Deferred实例建立另一个.done()回调。

现在,当ajax“done”回调运行时,dd(最初创建的Deferred对象)的值将被覆盖,并且t()返回Promise 。最终将运行.done()回调,但没有任何东西可以解析第一个Deferred实例,因此“外部”回调永远不会发生。

答案 1 :(得分:1)

因为未解析延迟对象。您正在创建2个延迟对象并解析其中一个。

答案 2 :(得分:0)

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

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

var dd = $.Deferred();

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

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

但是,当你的ajax函数完成后,你用这一行为变量dd分配一个不同的promise:

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. 将另一项活动与该承诺联系起来。
  3. .then()处理程序返回另一个承诺,使序列也等待该承诺。
  4. 从主函数返回链式承诺,以便您可以看到所有内容都已完成。