Javascript 将一组延迟传递给 $.when()

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5627284/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 18:08:25  来源:igfitidea点击:

Pass in an array of Deferreds to $.when()

javascriptjqueryargument-passingjquery-deferred.when

提问by adamjford

Here's an contrived example of what's going on: http://jsfiddle.net/adamjford/YNGcm/20/

这是一个人为的例子:http: //jsfiddle.net/adamjford/YNGcm/20/

HTML:

HTML:

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

JavaScript:

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>");
        });
    });
});

I want "All done!" to appear after all of the deferred tasks have completed, but $.when()doesn't appear to know how to handle an array of Deferred objects. "All done!" is happening first because the array is not a Deferred object, so jQuery goes ahead and assumes it's just done.

我要“全部完成!” 在所有延迟任务完成后$.when()出现,但似乎不知道如何处理延迟对象数组。“全部完成!” 因为数组不是 Deferred 对象,所以首先发生,所以 jQuery 继续并假设它刚刚完成。

I know one could pass the objects into the function like $.when(deferred1, deferred2, ..., deferredX)but it's unknown how many Deferred objects there will be at execution in the actual problem I'm trying to solve.

我知道可以将对象传递给函数,$.when(deferred1, deferred2, ..., deferredX)但不知道在我试图解决的实际问题中将有多少 Deferred 对象在执行。

回答by Alnitak

To pass an array of values to anyfunction that normally expects them to be separate parameters, use Function.prototype.apply, so in this case you need:

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

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

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

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

In ES6, you can use the ...spread operatorinstead:

在 ES6 中,您可以改用...展开运算符

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

In either case, since it's unlikely that you'll known in advance how many formal parameters the .thenhandler will require, that handler would need to process the argumentsarray in order to retrieve the result of each promise.

在任何一种情况下,由于您不可能事先知道.then处理程序需要多少正式参数,因此该处理程序需要处理arguments数组以检索每个承诺的结果。

回答by crispyduck

The workarounds above (thanks!) don't properly address the problem of getting back the objects provided to the deferred's resolve()method because jQuery calls the done()and fail()callbacks with individual parameters, not an array. That means we have to use the argumentspseudo-array to get all the resolved/rejected objects returned by the array of deferreds, which is ugly:

上面的解决方法(谢谢!)没有正确解决取回提供给 deferredresolve()方法的对象的问题,因为 jQuery使用单个参数而不是数组调用done()fail()回调。这意味着我们必须使用arguments伪数组来获取延迟数组返回的所有已解决/拒绝的对象,这很丑陋:

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

Since we passed in an array of deferreds, it would be nice to get back an array of results. It would also be nice to get back an actual array instead of a pseudo-array so we can use methods like Array.sort().

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

Here is a solution inspired by when.js's when.all()method that addresses these problems:

这里是通过激发溶液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)]);
                });
        });
    }
}

Now you can simply pass in an array of deferreds/promises and get back an array of resolved/rejected objects in your callback, like so:

现在你可以简单地传入一个延迟/承诺数组并在你的回调中返回一个已解决/拒绝的对象数组,如下所示:

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

回答by Eli

You can apply the whenmethod to your array:

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

var arr = [ /* Deferred objects */ ];

$.when.apply($, arr);

How do you work with an array of jQuery Deferreds?

你如何使用 jQuery Deferreds 数组?

回答by vinayakj

When calling multiple parallel AJAX calls, you have two options for handling the respective responses.

当调用多个并行 AJAX 调用时,您有两个选项来处理各自的响应。

  1. Use Synchronous AJAX call/ one after another/ not recommended
  2. Use Promises'array and $.whenwhich accepts promises and its callback .donegets called when all the promises are return successfully with respective responses.
  1. 使用同步 AJAX 调用/一个接一个/不推荐
  2. 使用Promises'数组并$.when接受promises 并且.done当所有promises 成功返回并带有相应的响应时,将调用其回调。

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>

回答by Gone Coding

As a simple alternative, that does not require $.when.applyor an array, you can use the following pattern to generate a single promise for multiple parallel promises:

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

promise = $.when(promise, anotherPromise);

e.g.

例如

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>");
        });
    });
});

Notes:

笔记:

  • I figured this one out after seeing someone chain promises sequentially, using promise = promise.then(newpromise)
  • The downside is it creates extra promise objects behind the scenes and any parameters passed at the end are not very useful (as they are nested inside additional objects). For what you want though it is short and simple.
  • The upside is it requires no array or array management.
  • 在看到有人按顺序使用链式承诺后,我想出了这个 promise = promise.then(newpromise)
  • 缺点是它在幕后创建了额外的 promise 对象,并且最后传递的任何参数都不是很有用(因为它们嵌套在其他对象中)。对于你想要的,虽然它简短而简单。
  • 好处是它不需要阵列或阵列管理。

回答by Volodymyr Yasinskyi

I want to propose other one with using $.each:

我想提出另一个使用 $.each 的建议:

  1. We may to declare ajax function like:

    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;
        }
    }
    
  2. Part of code where we creating array of functions with ajax to send:

    var arrayOfFn = [];
    for (var i = 0; i < someDataArray.length; i++) {
        var ajaxFnForArray = new ajaxFn(someDataArray[i]);
        arrayOfFn.push(ajaxFnForArray);
    }
    
  3. And calling functions with sending ajax:

    $.when(
        $.each(arrayOfFn, function(index, value) {
            value.call()
        })
    ).then(function() {
            alert("Cheer!");
        }
    )
    
  1. 我们可以像这样声明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;
        }
    }
    
  2. 我们使用 ajax 创建函数数组的部分代码:

    var arrayOfFn = [];
    for (var i = 0; i < someDataArray.length; i++) {
        var ajaxFnForArray = new ajaxFn(someDataArray[i]);
        arrayOfFn.push(ajaxFnForArray);
    }
    
  3. 并通过发送 ajax 调用函数:

    $.when(
        $.each(arrayOfFn, function(index, value) {
            value.call()
        })
    ).then(function() {
            alert("Cheer!");
        }
    )
    

回答by relic

If you're transpiling and have access to ES6, you can use spread syntax which specifically applies each iterable item of an object as a discrete argument, just the way $.when()needs it.

如果您正在转译并且可以访问 ES6,则可以使用扩展语法,该语法专门将对象的每个可迭代项作为离散参数应用,这正是$.when()需要它的方式。

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

MDN Link - Spread Syntax

MDN 链接 - 传播语法

回答by Cameron Forward

I had a case very similar where I was posting in an each loop and then setting the html markup in some fields from numbers received from the ajax. I then needed to do a sum of the (now-updated) values of these fields and place in a total field.

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

Thus the problem was that I was trying to do a sum on all of the numbers but no data had arrived back yet from the async ajax calls. I needed to complete this functionality in a few functions to be able to reuse the code. My outer function awaits the data before I then go and do some stuff with the fully updated DOM.

因此,问题是我试图对所有数字求和,但没有数据从异步 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;
    }

回答by mastaBlasta

If you're using angularJS or some variant of the Q promise library, then you have a .all()method that solves this exact problem.

如果您使用的是 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){...}
);

see the full API:

查看完整的 API:

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

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

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

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