Javascript 如何等待一组异步回调函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10004112/
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
How can I wait for set of asynchronous callback functions?
提问by codersarepeople
I have code that looks something like this in javascript:
我的代码在 javascript 中看起来像这样:
forloop {
//async call, returns an array to its callback
}
After ALL of those async calls are done, I want to calculate the min over all of the arrays.
在所有这些异步调用完成后,我想计算所有数组的最小值。
How can I wait for all of them?
我怎么能等到他们呢?
My only idea right now is to have an array of booleans called done, and set done[i] to true in the ith callback function, then say while(not all are done) {}
我现在唯一的想法是有一个名为 done 的布尔数组,并在第 i 个回调函数中将 done[i] 设置为 true,然后说 while(并非全部都完成了){}
edit: I suppose one possible, but ugly solution, would be to edit the done array in each callback, then call a method if all other done are set from each callback, thus the last callback to complete will call the continuing method.
编辑:我想一个可能但丑陋的解决方案是在每个回调中编辑完成的数组,然后调用一个方法,如果所有其他完成从每个回调中设置,因此最后一个完成的回调将调用继续方法。
Thanks in advance.
提前致谢。
回答by jfriend00
You haven't been very specific with your code, so I'll make up a scenario. Let's say you have 10 ajax calls and you want to accumulate the results from those 10 ajax calls and then when they have all completed you want to do something. You can do it like this by accumulating the data in an array and keeping track of when the last one has finished:
你的代码不是很具体,所以我会编一个场景。假设您有 10 个 ajax 调用,并且您想累积这 10 个 ajax 调用的结果,然后当它们全部完成时,您想要做一些事情。您可以通过在数组中累积数据并跟踪最后一个完成的时间来做到这一点:
Manual Counter
手动计数器
var ajaxCallsRemaining = 10;
var returnedData = [];
for (var i = 0; i < 10; i++) {
doAjax(whatever, function(response) {
// success handler from the ajax call
// save response
returnedData.push(response);
// see if we're done with the last ajax call
--ajaxCallsRemaining;
if (ajaxCallsRemaining <= 0) {
// all data is here now
// look through the returnedData and do whatever processing
// you want on it right here
}
});
}
Note: error handling is important here (not shown because it's specific to how you're making your ajax calls). You will want to think about how you're going to handle the case when one ajax call never completes, either with an error or gets stuck for a long time or times out after a long time.
注意:错误处理在这里很重要(未显示,因为它特定于您如何进行 ajax 调用)。您将需要考虑如何处理当一个 ajax 调用永远不会完成时的情况,要么出现错误,要么卡住很长时间或很长时间后超时。
jQuery Promises
jQuery 承诺
Adding to my answer in 2014. These days, promises are often used to solve this type of problem since jQuery's $.ajax()
already returns a promise and $.when()
will let you know when a group of promises are all resolved and will collect the return results for you:
添加到我在 2014 年的回答中。如今,promise 通常用于解决此类问题,因为 jQuery$.ajax()
已经返回一个 promise,并且$.when()
会在一组 promise 全部解决时通知您,并为您收集返回结果:
var promises = [];
for (var i = 0; i < 10; i++) {
promises.push($.ajax(...));
}
$.when.apply($, promises).then(function() {
// returned data is in arguments[0][0], arguments[1][0], ... arguments[9][0]
// you can process it here
}, function() {
// error occurred
});
ES6 Standard Promises
ES6 标准承诺
As specified in kba's answer: if you have an environment with native promises built-in (modern browser or node.js or using babeljs transpile or using a promise polyfill), then you can use ES6-specified promises. See this tablefor browser support. Promises are supported in pretty much all current browsers, except IE.
正如 kba 的回答中所指定的:如果您有一个内置本机承诺的环境(现代浏览器或 node.js 或使用 babeljs transpile 或使用承诺 polyfill),那么您可以使用 ES6 指定的承诺。请参阅此表以了解浏览器支持。几乎所有当前浏览器都支持 Promise,但 IE 除外。
If doAjax()
returns a promise, then you can do this:
如果doAjax()
返回一个承诺,那么你可以这样做:
var promises = [];
for (var i = 0; i < 10; i++) {
promises.push(doAjax(...));
}
Promise.all(promises).then(function() {
// returned data is in arguments[0], arguments[1], ... arguments[n]
// you can process it here
}, function(err) {
// error occurred
});
If you need to make a non-promise async operation into one that returns a promise, you can "promisify" it like this:
如果您需要将非承诺异步操作变成一个返回承诺的操作,您可以像这样“承诺”它:
function doAjax(...) {
return new Promise(function(resolve, reject) {
someAsyncOperation(..., function(err, result) {
if (err) return reject(err);
resolve(result);
});
});
}
And, then use the pattern above:
然后使用上面的模式:
var promises = [];
for (var i = 0; i < 10; i++) {
promises.push(doAjax(...));
}
Promise.all(promises).then(function() {
// returned data is in arguments[0], arguments[1], ... arguments[n]
// you can process it here
}, function(err) {
// error occurred
});
Bluebird Promises
蓝鸟承诺
If you use a more feature rich library such as the Bluebird promise library, then it has some additional functions built in to make this easier:
如果您使用功能更丰富的库,例如Bluebird 承诺库,那么它会内置一些附加功能来简化此操作:
var doAjax = Promise.promisify(someAsync);
var someData = [...]
Promise.map(someData, doAjax).then(function(results) {
// all ajax results here
}, function(err) {
// some error here
});
回答by kba
Checking in from 2015: We now have native promisesin most recent browser(Edge 12, Firefox 40, Chrome 43, Safari 8, Opera 32 and Android browser 4.4.4 and iOS Safari 8.4, but not Internet Explorer, Opera Mini and older versions of Android).
从 2015 年开始签入:我们现在在最新的浏览器(Edge 12、Firefox 40、Chrome 43、Safari 8、Opera 32 和 Android 浏览器 4.4.4 和 iOS Safari 8.4,但不是 Internet Explorer、Opera Mini 和旧版本)中提供本机承诺安卓)。
If we want to perform 10 async actions and get notified when they've all finished, we can use the native Promise.all
, without any external libraries:
如果我们想执行 10 个异步操作并在它们全部完成时得到通知,我们可以使用 native Promise.all
,而无需任何外部库:
function asyncAction(i) {
return new Promise(function(resolve, reject) {
var result = calculateResult();
if (result.hasError()) {
return reject(result.error);
}
return resolve(result);
});
}
var promises = [];
for (var i=0; i < 10; i++) {
promises.push(asyncAction(i));
}
Promise.all(promises).then(function AcceptHandler(results) {
handleResults(results),
}, function ErrorHandler(error) {
handleError(error);
});
回答by Paul
You can use jQuery's Deferredobject along with the whenmethod.
您可以将 jQuery 的Deferred对象与when方法一起使用。
deferredArray = [];
forloop {
deferred = new $.Deferred();
ajaxCall(function() {
deferred.resolve();
}
deferredArray.push(deferred);
}
$.when(deferredArray, function() {
//this code is called after all the ajax calls are done
});
回答by Eugene Retunsky
You can emulate it like this:
你可以像这样模拟它:
countDownLatch = {
count: 0,
check: function() {
this.count--;
if (this.count == 0) this.calculate();
},
calculate: function() {...}
};
then each async call does this:
然后每个异步调用执行以下操作:
countDownLatch.count++;
while in each asynch call back at the end of the method you add this line:
在方法末尾的每个异步回调中,您添加以下行:
countDownLatch.check();
In other words, you emulate a count-down-latch functionality.
换句话说,您模拟了倒计时锁存功能。
回答by philx_x
This is the most neat way in my opinion.
这是我认为最简洁的方式。
(for some reason Array.map doesn't work inside .then functions for me. But you can use a .forEach and [].concat() or something similar)
(出于某种原因,Array.map 不能在 .then 函数中工作。但你可以使用 .forEach 和 [].concat() 或类似的东西)
Promise.all([
fetch('/user/4'),
fetch('/user/5'),
fetch('/user/6'),
fetch('/user/7'),
fetch('/user/8')
]).then(responses => {
return responses.map(response => {response.json()})
}).then((values) => {
console.log(values);
})