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

时间:2016-07-09 09:29:35

标签: jquery jquery-deferred

尽管这个主题有很多答案,但我仍然不太了解$.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())后的{1}}:

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

问题

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

1 个答案:

答案 0 :(得分: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承诺而不是jQuery承诺,那么您的function1().then()将继续正常工作。

  3. 在jQuery中,Ajax请求已经返回一个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函数或异步操作,那么你可以使用promises来链接它们(它们将对它们进行排序)或协调它们,并仍然返回一个代表何时完成所有代表的单个promise期望的最终结果。 jQuery提供了诸如$.when()之类的协调工具,以便知道何时完成了多个promise(这是ES6标准中的Promise.all())。