[英]How can I take advantage of callback functions for asynchronous XMLHttpRequest?
[英]How can I wait for set of asynchronous callback functions?
我在 javascript 中的代码看起来像这样:
forloop {
//async call, returns an array to its callback
}
在完成所有这些异步调用后,我想计算所有 arrays 的最小值。
我怎么能等到他们所有人呢?
我现在唯一的想法是有一个名为 done 的布尔数组,并在第 i 个回调 function 中将 done[i] 设置为 true,然后说 while(not all are done) {}
编辑:我想一个可能但丑陋的解决方案是在每个回调中编辑 done 数组,然后如果所有其他完成都从每个回调中设置,则调用一个方法,因此最后一个完成的回调将调用继续方法。
你的代码不是很具体,所以我会编一个场景。 假设您有 10 个 ajax 调用,您想要累积这 10 个 ajax 调用的结果,然后当它们全部完成时您想要做某事。 您可以通过在数组中累积数据并跟踪最后一个完成的时间来这样做:
手动计数器
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
}
});
}
注意:错误处理在这里很重要(未显示,因为它特定于您如何进行 ajax 调用)。 您将要考虑如何处理一个 ajax 调用永远不会完成的情况,要么出错,要么卡住很长时间,要么在很长时间后超时。
jQuery 承诺
添加到我在 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 标准承诺
正如 kba 的回答中所指定的那样:如果你有一个内置原生承诺的环境(现代浏览器或 node.js 或使用 babeljs transpile 或使用 promise polyfill),那么你可以使用 ES6 指定的承诺。 有关浏览器支持,请参阅此表。 几乎所有当前的浏览器都支持 Promises,除了 IE。
如果doAjax()
返回 promise,那么您可以这样做:
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
});
如果您需要将非承诺异步操作转换为返回 promise 的异步操作,您可以像这样“承诺”它:
function doAjax(...) {
return new Promise(function(resolve, reject) {
someAsyncOperation(..., function(err, result) {
if (err) return reject(err);
resolve(result);
});
});
}
并且,然后使用上面的模式:
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 promise 库,那么它有一些内置的附加功能可以使这更容易:
var doAjax = Promise.promisify(someAsync);
var someData = [...]
Promise.map(someData, doAjax).then(function(results) {
// all ajax results here
}, function(err) {
// some error here
});
从 2015 年开始检查:我们现在在最新的浏览器(Edge 12、Firefox 40、Chrome 43、Safari 8、Opera 32 和 Android 浏览器 4.4.4 和 iOS 8837867555.4.4 和 iOS 8837867555.4.4 和 iOS,而不是 Mini 和 Explorer In Explorer旧版本的 Android)。
如果我们想要执行 10 个异步操作并在它们全部完成时得到通知,我们可以使用原生的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);
});
你可以像这样模拟它:
countDownLatch = {
count: 0,
check: function() {
this.count--;
if (this.count == 0) this.calculate();
},
calculate: function() {...}
};
然后每个异步调用都这样做:
countDownLatch.count++;
在方法末尾的每个异步回调中,您添加以下行:
countDownLatch.check();
换句话说,您模拟了倒计时锁存功能。
在我看来,这是最简洁的方法。
(出于某种原因,Array.map 在内部不起作用。然后对我起作用。但是您可以使用 a.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);
})
像after
一样使用控制流库
after.map(array, function (value, done) {
// do something async
setTimeout(function () {
// do something with the value
done(null, value * 2)
}, 10)
}, function (err, mappedArray) {
// all done, continue here
console.log(mappedArray)
})
在 Node.js 你可以使用 async/await 来控制异步流程
示例代码:
const foo = async () => {
try {
const ids = [100, 101, 102];
const fetchFromExternalApi = util.promisify(fetchFromExternalApiCallback);
const promises = ids.map((id) => fetchFromExternalApi(id));
const dataList = await Promise.resolve(promises); // dataList is an array
return dataList;
} catch (err) {
// error handling
}
};
我看到了Promise.all()
的几个响应,但是如果任何 promise 产生异常,这个 function 就会停止......
2022 年的最佳解决方案是Promise.allSettled()
(此处的文档: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
快速示例:
const allPromises = [];
for (r in records) {
const promise = update_async(r);
allPromises.push(promise);
};
await Promise.allSettled(allPromises);
最后,您使用allPromises
获得一个数组,其中包含每个 promise 的结果:
{status: “fulfilled”, value: xxx }
{status: "rejected", reason: Error: an error}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.