首页 文章

将Deferred数组传递给$ .when()

提问于
浏览
408

这里's an contrived example of what'正在进行中:http://jsfiddle.net/adamjford/YNGcm/20/

HTML:

<a href="#">Click me!</a>
<div></div>

JavaScript的:

function getSomeDeferredStuff() {
    var deferreds = [];

    var i = 1;
    for (i = 1; i <= 10; i++) {
        var count = i;

        deferreds.push(
        $.post('/echo/html/', {
            html: "<p>Task #" + count + " complete.",
            delay: count
        }).success(function(data) {
            $("div").append(data);
        }));
    }

    return deferreds;
}

$(function() {
    $("a").click(function() {
        var deferreds = getSomeDeferredStuff();

        $.when(deferreds).done(function() {
            $("div").append("<p>All done!</p>");
        });
    });
});

我想在所有延期任务完成后出现"All done!",但是 $.when() 并没有完成 .

我知道可以将对象传递给像 $.when(deferred1, deferred2, ..., deferredX) 这样的函数,但是它试图解决 .

9 回答

  • 38

    要将值数组传递给通常期望它们为单独参数的任何函数,请使用 Function.prototype.apply ,因此在这种情况下,您需要:

    $.when.apply($, my_array).then( ___ );
    

    http://jsfiddle.net/YNGcm/21/

    在ES6中,您可以使用 ... spread operator代替:

    $.when(...my_array).then( ___ );
    

    在任何一种情况下,由于事先知道 .then 处理程序需要多少个形式参数,该处理程序需要处理 arguments 数组以便检索每个promise的结果 .

  • 6

    如果你正在使用angularJS或Q promise库的某些变体,那么你有一个 .all() 方法可以解决这个问题 .

    var savePromises = [];
    angular.forEach(models, function(model){
      savePromises.push(
        model.saveToServer()
      )
    });
    
    $q.all(savePromises).then(
      function success(results){...},
      function failed(results){...}
    );
    

    查看完整的API:

    https://github.com/kriskowal/q/wiki/API-Reference#promiseall

    https://docs.angularjs.org/api/ng/service/$q

  • 1

    上面的解决方法(谢谢!)don 't properly address the problem of getting back the objects provided to the deferred' s resolve() 方法,因为jQuery使用单个参数调用 done()fail() 回调,而不是数组 . 这意味着我们必须使用 arguments 伪数组来获取延迟数组返回的所有已解析/被拒绝的对象,这很丑陋:

    $.when.apply($,deferreds).then(function() {
         var objects=arguments; // The array of resolved objects as a pseudo-array
         ...
    };
    

    由于我们传入了一个延迟数组,因此返回一组结果会很好 . 回到实际的数组而不是伪数组也很好,所以我们可以使用像 Array.sort() 这样的方法 .

    这是一个灵感来自 when.jswhen.all() 方法的解决方案,解决了这些问题:

    // Put somewhere in your scripting environment
    if (typeof jQuery.when.all === 'undefined') {
        jQuery.when.all = function (deferreds) {
            return $.Deferred(function (def) {
                $.when.apply(jQuery, deferreds).then(
                    function () {
                        def.resolveWith(this, [Array.prototype.slice.call(arguments)]);
                    },
                    function () {
                        def.rejectWith(this, [Array.prototype.slice.call(arguments)]);
                    });
            });
        }
    }
    

    现在你可以简单地传入一个deferreds / promises数组,并在你的回调函数中找回一系列已解析/被拒绝的对象,如下所示:

    $.when.all(deferreds).then(function(objects) {
        console.log("Resolved objects:", objects);
    });
    
  • 99

    您可以将 when 方法应用于您的数组:

    var arr = [ /* Deferred objects */ ];
    
    $.when.apply($, arr);
    

    How do you work with an array of jQuery Deferreds?

  • 3

    调用多个并行AJAX调用时,您有两个选项来处理相应的响应 .

    • 使用同步AJAX呼叫/一个接一个/不推荐

    • 使用 Promises' 数组和$.when接受 promise 并且当所有 promise 成功返回各自的响应时,将调用其回调 .done .

    Example

    function ajaxRequest(capitalCity) {
       return $.ajax({
            url: 'https://restcountries.eu/rest/v1/capital/'+capitalCity,
            success: function(response) {
            },
            error: function(response) {
              console.log("Error")
            }
        });
    }
    $(function(){
       var capitalCities = ['Delhi', 'Beijing', 'Washington', 'Tokyo', 'London'];
       $('#capitals').text(capitalCities);
    
       function getCountryCapitals(){ //do multiple parallel ajax requests
          var promises = [];   
          for(var i=0,l=capitalCities.length; i<l; i++){
                var promise = ajaxRequest(capitalCities[i]);
                promises.push(promise);
          }
      
          $.when.apply($, promises)
            .done(fillCountryCapitals);
       }
      
       function fillCountryCapitals(){
            var countries = [];
            var responses = arguments;
            for(i in responses){
                console.dir(responses[i]);
                countries.push(responses[i][0][0].nativeName)
            }  
            $('#countries').text(countries);
       }
      
       getCountryCapitals()
    })
    
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div>
      <h4>Capital Cities : </h4> <span id="capitals"></span>
      <h4>Respective Country's Native Names : </h4> <span id="countries"></span>
    </div>
    
  • 0

    作为一个简单的替代方案,不需要 $.when.applyarray ,您可以使用以下模式为多个并行承诺生成单个承诺:

    promise = $.when(promise, anotherPromise);
    

    例如

    function GetSomeDeferredStuff() {
        // Start with an empty resolved promise (or undefined does the same!)
        var promise;
        var i = 1;
        for (i = 1; i <= 5; i++) {
            var count = i;
    
            promise = $.when(promise,
            $.ajax({
                type: "POST",
                url: '/echo/html/',
                data: {
                    html: "<p>Task #" + count + " complete.",
                    delay: count / 2
                },
                success: function (data) {
                    $("div").append(data);
                }
            }));
        }
        return promise;
    }
    
    $(function () {
        $("a").click(function () {
            var promise = GetSomeDeferredStuff();
            promise.then(function () {
                $("div").append("<p>All done!</p>");
            });
        });
    });
    

    注意:

    • 在使用 promise = promise.then(newpromise) 看到有人连锁承诺之后,我想出了这个

    • 缺点是它在幕后创建额外的承诺对象,并且在末尾传递的任何参数都不是很有用(因为它们嵌套在其他对象中) . 为了你想要的东西虽然它简短而简单 .

    • 好处是它不需要阵列或阵列管理 .

  • 5

    我想用$ .each建议另一个:

    • 我们可以声明ajax函数,如:
    function ajaxFn(someData) {
        this.someData = someData;
        var that = this;
        return function () {
            var promise = $.Deferred();
            $.ajax({
                method: "POST",
                url: "url",
                data: that.someData,
                success: function(data) {
                    promise.resolve(data);
                },
                error: function(data) {
                    promise.reject(data);
                }
            })
            return promise;
        }
    }
    
    • 我们使用ajax创建函数数组的部分代码:
    var arrayOfFn = [];
    for (var i = 0; i < someDataArray.length; i++) {
        var ajaxFnForArray = new ajaxFn(someDataArray[i]);
        arrayOfFn.push(ajaxFnForArray);
    }
    
    • 并通过发送ajax调用函数:
    $.when(
        $.each(arrayOfFn, function(index, value) {
            value.call()
        })
    ).then(function() {
            alert("Cheer!");
        }
    )
    
  • 0

    如果您正在进行转换并且可以访问ES6,则可以使用扩展语法,该语法将对象的每个可迭代项专门应用为离散参数,就像 $.when() 需要它一样 .

    $.when(...deferreds).done(() => {
        // do stuff
    });
    

    MDN Link - Spread Syntax

  • 678

    我有一个非常相似的案例,我在每个循环中发布,然后在某些字段中设置html标记,从ajax收到的数字 . 然后,我需要对这些字段的(现在更新的)值进行总和,并将其放在总字段中 .

    因此问题是我试图对所有数字进行求和,但是没有数据从异步ajax调用中返回 . 我需要在一些函数中完成此功能才能重用代码 . 在我用完全更新的DOM做一些事情之前,我的外部函数等待数据 .

    // 1st
        function Outer() {
            var deferreds = GetAllData();
    
            $.when.apply($, deferreds).done(function () {
                // now you can do whatever you want with the updated page
            });
        }
    
        // 2nd
        function GetAllData() {
            var deferreds = [];
            $('.calculatedField').each(function (data) {
                deferreds.push(GetIndividualData($(this)));
            });
            return deferreds;
        }
    
        // 3rd
        function GetIndividualData(item) {
            var def = new $.Deferred();
            $.post('@Url.Action("GetData")', function (data) {
                item.html(data.valueFromAjax);
                def.resolve(data);
            });
            return def;
        }
    

相关问题