jQuery Deferred/promise 没有按预期工作

Posted

技术标签:

【中文标题】jQuery Deferred/promise 没有按预期工作【英文标题】:jQuery Deferred/promise not working as expected 【发布时间】:2017-06-21 07:18:30 【问题描述】:

好的,我已经阅读了 1,000,000 多篇关于 jQuery 延迟和/或承诺的文章,但我仍然遇到了一些问题。

functionOne() 
    var deferred = $.Deferred();

    var request = $.ajax(
        url: 'http://example.com/mypath/etc'
    );

    request.done(function(data) 

        // TODO: I've got stuff here that takes a while.

        deferred.resolve();
    );

    return deferred.promise();


functionTwo() 
    // Something that depends on the others being finished.


$.when(
    functionOne(), 
    anotherLongRunningFunctionWithAjax())
 .then(
    functionTwo()
);

在“then”开始之前,我需要“when”中的任何函数来完全完成(.ajax done)。然而,promise 立即返回(如预期的那样),但 functionTwo 启动,即使 functionOne 没有调用“done”。

我确信这是对延迟调用和调用链的根本误解。

编辑:

function functionOne() 
    console.log('functionOne called');

    var request = $.ajax(
        url: 'http://example.com/mypath/etc'
    );

    request.done(function(data) 
        console.log('Starting Done.');

        setTimeout(function () 
            console.log('Done finished.');
        , 5000);
    );

    console.log('Returning promise.');
    return request;


function functionTwo() 
    console.log('functionTwo called');


$.when(functionOne()).then(functionTwo());

在控制台中给我这个:

functionOne called
Returning promise.
functionTwo called (should be called after Done is finished.)
Starting Done.
Done finished.

【问题讨论】:

var deferred = $.Deferred(); 不必要,你已经从 $.ajax 获得了一个。用 .then 将其链接起来。 您的逻辑应该可以正常工作,我猜// TODO: 部分实际上有代码并且没有正确延迟 .resolve() 调用。 @KevinB 老实说,我先尝试了没有额外的 $.Deferred,但我也无法让它工作。我确定我错过了一些简单的东西。 更新后的新代码不会在函数 1 的 setTimeout 完成时延迟函数 2,这是设计使然。对于该功能,您必须使用 .then 而不是 .done 并创建一个新的 Promise 以返回 .then ,它会在 setTimeout 完成时解决。此外,单个 Promise 不需要 $.when,但它也不一定有害。 @KevinB 但是无论我使用的是 setTimeout 还是其他东西,至少不会出现“开始完成”吗?我实际上并没有使用 setTimeout,而是加载了一个下拉列表。其实可以完全注释掉setTimeout,“Starting done”还是最后出现的。 【参考方案1】:

在您编辑的代码中,有两个问题:

    functionOne 中的计时器在 request 解决后启动,但您返回 request。所以无论计时器发生什么......它与返回的承诺无关,当时已经解决了。

    您立即调用 functionTwo,而不是传递 $.when 回调的函数引用

这是工作代码:

function functionOne() 
    console.log('functionOne called');
    console.log('Returning promise.');
    return $.ajax(
        url: 'https://jsonplaceholder.typicode.com/posts/1'
    ).then(function(data) 
        console.log('Starting Done.');
        var dfd = $.Deferred();
        setTimeout(function () 
            console.log('Done finished.');
            dfd.resolve(data); // indicate when you are done
        , 2000); // 2 seconds will do ;-)
        return dfd.promise(); // you need to return a promise again
    );


function functionTwo() 
    console.log('functionTwo called');


// don't call functionTwo immediately, leave that to the promise to do:
$.when(functionOne()).then(functionTwo);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

【讨论】:

另外,供将来参考(供我自己参考):如果我在“then”中为 functionTwo 添加括号(例如 .then(function())),该函数仍将立即触发。因此,如果 functionTwo 中需要参数,请创建一个包含函数指针的 var。例如var f2 = function() functionTwo(param); 并将指针传递给“.then(f2)”。或者,.then(function() functionTwo(param); ); 添加参数也可以使用bind:.then(functionTwo.bind(null, param))【参考方案2】:

你是using an anti-pattern,因为$.ajax 本身会返回一个承诺

做事

functionOne() 

    var request = $.ajax(
        url: 'http://example.com/mypath/etc'
    );

    request.done(function(data) 
        // TODO: I've got stuff here that takes a while.       
    );

    return request

【讨论】:

不知道是什么“我这里有东西需要一段时间” 是不是很难提供更多帮助 我的提问被否决了?我问是因为我不明白。

以上是关于jQuery Deferred/promise 没有按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

通过 ES6 Promise 和 jQuery Deferred 的异同学习 Promise

jquery的defer

JQuery 的 deferred . promise对象

jQuery Deferred/promise 没有按预期工作

为啥在两次调用 promise 时 RSVP Deferred 会产生错误

jQuery 的延迟回调执行序列