如何将延迟对象应用于大块代码?

How to apply a deferred object to a large block of code?

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

我想更好地理解它们并避免任何 "anti-patterns"。

我已阅读:

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

而"sticks"的是,它是一个对象,它有可用的方法,例如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!');
});

这似乎有四个组成部分:

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

我可以通过 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() 问题的一个很好、简短、简单的回答,我理解它的逻辑,但似乎不适用于我的场景(例如,我不能只是链接简单的fadeOut()之后的promise()):

问题

在上面的大型函数示例中,在 ajax 请求等完成执行后调用 done() 需要遵循什么约定?

首先,我建议更改此设置:

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 promise 标准与 new Promise() 的工作方式,因此将您的编程朝着标准的方向发展通常是个好主意.

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


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

Promises 没有魔力,只知道异步事情何时完成。相反,一些代码必须在异步操作完成时专门调用 .resolve()。而且,在 jQuery Ajax 函数的情况下,这是自动为您完成的,并承诺任何 jQuery Ajax 函数 return。所以,你可以这样做:

function largeFunction(myVar) {

     return $.ajax(...);

}

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

如果你在 largeFunction() 中有多个 Ajax 函数或异步操作,那么你可以使用 promises 链接它们(这将对它们进行排序)或协调它们并且仍然 return单一的承诺,代表一切何时完成并代表期望的最终结果。 jQuery 提供协调工具,例如 $.when() 以了解何时完成多个承诺(这是 ES6 标准中的 Promise.all())。