【问题标题】:How to apply a deferred object to a large block of code?如何将延迟对象应用于一大块代码?
【发布时间】:2016-07-09 09:29:35
【问题描述】:

尽管这里有很多关于该主题的答案,但我仍然不太了解$.Deferred() 对象。

我想更好地理解它们并避免任何“反模式”。

我已阅读:

https://api.jquery.com/category/deferred-object/

而“坚持”的是它是一个对象,它具有可用的方法,例如done()

我尝试根据this answer 创建最简单的示例:

jsFiddle Link

function function1() {
    // create a deferred object first
    var dfrd1 = $.Deferred();

    // mimic an async request
    setTimeout(function() {
        // lots of async stuff here
        // resolve the deferred object 
        dfrd1.resolve();
    }, 1000);

    // return a promise()
    return dfrd1.promise();
}

function1().done(function() {
    // when function1 is done, do something
    console.log('function1 is done!');
});

这似乎有四个组成部分:

  • 创建一个$.Deferred() 对象。
  • resolve() 在异步代码块之后。
  • 在异步代码块之后返回promise()
  • 调用done()方法。

上面的示例似乎按预期工作,但是当我尝试将此逻辑应用于包含 $.ajax() 请求的大型函数时,以便在加载后将类应用于内容时,该类不是应用。

我可以通过 firebug 运行 addClass() 代码,因此该代码没有任何问题。

大函数场景的伪代码为:

function largeFunction(myVar) {

    // create a deferred object first
    var dfrd1 = $.Deferred();

    // lots of code and an ajax request here

    //  resolve the object and return a promise
    //  (this seems naive to hope resolve will
    //  know when everything above has completed)
    dfrd1.resolve();
    return dfrd1.promise();

}

// call the done() method from an on click event

$(document).on("click", ".some_class", function() {

    largeFunction(myVar).done(function() {

        console.log('largeFunction(myVar) is done, you can now add class');

        var my_classes = $(".my_class");

        $.each(classes, function(index, value) {
            $(this).addClass("another_class");
        });

    });

});

以下是对$.Deferred() 问题的良好、简短、简单的回答,我理解其逻辑,但似乎不适用于我的场景(例如,我不能只链接promise()经过简单的fadeOut()):

https://stackoverflow.com/a/24234881/1063287

问题

在上面的大型函数示例中,我需要遵循哪些约定才能在 ajax 请求等执行完毕后调用done()

【问题讨论】:

  • 简单 - 在 ajax 调用的 .then 方法中解析 dfd(或返回 ajax 调用本身)。
  • @Adam 或者您可以从 $.ajax 调用本身返回承诺。无论哪种方式,您都应该将其添加为答案

标签: jquery jquery-deferred


【解决方案1】:

首先,我建议改变这个:

function function1() {
    // create a deferred object first
    var dfrd1 = $.Deferred();

    // mimic an async request
    setTimeout(function() {
        // lots of async stuff here
        // resolve the deferred object 
        dfrd1.resolve();
    }, 1000);

    // return a promise()
    return dfrd1.promise();
}

function1().done(function() {
    // when function1 is done, do something
    console.log('function1 is done!');
});

到这里:

function function1() {
    // create a deferred object first
    return $.Deferred(function(def) {
        // mimic an async request
        setTimeout(function() {
            // lots of async stuff here
            // resolve the deferred object 
            def.resolve();
        }, 1000);
    }).promise();

}

function1().then(function() {
    // when function1 is done, do something
    console.log('function1 is done!');
});

您的原始代码运行良好且不包含反模式,但此新代码具有以下优点:

  1. 使用对$.Deferred() 的回调更接近于 ES6 承诺标准与 new Promise() 的工作方式,因此将您的编程朝着标准的方向移动通常是个好主意。

  2. 使用.then() 而不是.done()。同样,.then() 是 ES6 承诺标准的工作方式,而 jQuery 也很好地支持它。如果在未来某个时间点,您将 function1() 更改为使用实际的 ES6 Promise 而不是 jQuery Promise,那么您的 function1().then() 将继续正常工作。


在 jQuery 中,Ajax 请求已经返回了一个 Promise。没有必要将它包装在 deferred 中,事实上,这样做是一种反模式(当你不需要创建一个新的时创建一个 Promise)。

Promise 没有神奇的力量来知道异步事情何时完成。相反,一些代码必须在异步操作完成时专门调用.resolve()。而且,对于 jQuery Ajax 函数,这是自动为您完成的,并承诺任何 jQuery Ajax 函数都会返回。所以,你可以这样做:

function largeFunction(myVar) {

     return $.ajax(...);

}

largeFunction(....).then(function(results) {
    // handle successful results here
}, function(jqXHR, textStatus, errorThrown ) {
    // handle error here
});

如果您在largeFunction() 中有多个 Ajax 函数或异步操作,那么您可以使用 Promise 将它们链接起来(这将对它们进行排序)或协调它们,并且仍然返回一个 Promise,它表示一切都完成并表示所需最终结果。 jQuery 提供了诸如$.when() 之类的协调工具来了解多个promise 何时完成(这是ES6 标准中的Promise.all())。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-01
    • 1970-01-01
    • 2020-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-01
    • 2011-12-06
    相关资源
    最近更新 更多