我有一个函数foo
这使得在一个循环中的多个(并行)异步调用。我需要以某种等到所有的呼叫的结果是可用的。如何从foo
返回的全部结果,否则之后的所有数据的可引发一些处理?
我尝试添加每个结果到一个数组,但随后的数组不填充直到哪里需要使用它的点之后。
function foo() {
var results = [];
for (var i = 0; i < 10; i++) {
someAsyncFunction({someParam:i}, function callback(data) {
results.push(data);
});
}
return results;
}
var result = foo(); // It always ends up being an empty array at this point.
注意:这个问题是沿着现有的通用"How do I return the response from an asynchronous call?" question的线条刻意仿制。这个问题有一些优秀的答案,但没有涉及到多个异步调用。有迹象表明,提及多个呼叫其他一些问题,但我找不到任何环路为基础的,有的只有jQuery的答案,等我希望这里是不依赖于某个库的一些通用的技巧。
使用的承诺。准确地说,Promise.all
是专为这一点。
这需要承诺的阵列(或迭代),并返回一个新的承诺,当阵列的所有承诺都得到了解决,其解决。否则,当阵列中的任何承诺,拒绝它拒绝。
function someAsyncFunction(data, resolve, reject) {
setTimeout(function() {
if(Math.random() < .05) {
// Suppose something failed
reject('Error while processing ' + data.someParam);
} else {
// Suppose the current async work completed succesfully
resolve(data.someParam);
}
}, Math.random() * 1000);
}
function foo() {
// Create an array of promises
var promises = [];
for (var i = 0; i < 10; i++) {
// Fill the array with promises which initiate some async work
promises.push(new Promise(function(resolve, reject) {
someAsyncFunction({someParam:i}, resolve, reject);
}));
}
// Return a Promise.all promise of the array
return Promise.all(promises);
}
var result = foo().then(function(results) {
console.log('All async calls completed successfully:');
console.log(' --> ', JSON.stringify(results));
}, function(reason) {
console.log('Some async call failed:');
console.log(' --> ', reason);
});
请注意,结果将根据承诺的阵列,而不是在为了使承诺是在解决的顺序给出。
这样做的一个简单的方法是将触发回调一旦所有的反应都是在数组中:
function foo(cb) {
var results = [];
for (var i = 0; i < 10; i++) {
someAsyncFunction({someParam:i}, function callback(data) {
results.push(data);
if(results.length===10){
cb(results);
}
});
}
}
foo(function(resultArr){
// do whatever with array of results
});
从Promise.all
方法唯一的区别是结果不保证的顺序;但就是有一些补充容易实现。
很久以前我在这里回答了一个非常类似的问题:Coordinating parallel execution in node.js。
然而,时代已经转移。从那时起一个非常好的图书馆出现了,并承诺设计模式已经被充分挖掘,甚至标准化到的langauge。如果你想看看如何将其与原始代码进行点击上面的链接。如果你只是想编写阅读..
该async.js library已基本实现在上面的链接代码。随着异步你会写会看起来像这样的代码:
var listOfAsyncFunctions = [];
for (var i = 0; i < 10; i++) {
(function(n){
// Construct an array of async functions with the expected
// function signature (one argument that is the callback).
listOfAsyncFunctions.push(function(callback){
// Note: async expects the first argument to callback to be an error
someAsyncFunction({someParam:n}, function (data) {
callback(null,data);
});
})
})(i); // IIFE to break the closure
}
// Note that at this point you haven't called the async functions.
// Pass the array to async.js and let it call them.
async.parallel(listOfAsyncFunctions,function (err,result) {
console.log(result); // result will be the same order as listOfAsyncFunctions
});
然而,async.js的作者做了不止于此。异步还具有功能类似阵列的操作:每个,地图,过滤,减少。这使得异步处理阵列简单,可以方便地代码来理解:
var listOfParams = [];
for (var i = 0; i < 10; i++) {
// Construct an array of params:
listOfParams.push({someParam:i});
}
async.map(listOfParams,someAsyncFunction,function (err,result) {
console.log(result);
});
另一件事异步给你是如何处理异步任务不同的算法。例如说要刮网站,但不想让他们禁止你的IP地址发送垃圾邮件他们的服务器。您可以使用async.series()
代替parallel
来处理任务,一次一个:
// Set-up listOfAsyncFunctions as above
async.series(listOfAsyncFunctions,function (err,result) {
console.log(result); // result will be the same order as listOfAsyncFunctions
});
或者,如果你想一次处理3项任务:
async. parallelLimit(listOfAsyncFunctions, 3, function (err,result) {
console.log(result); // result will be the same order as listOfAsyncFunctions
});
该Promise.all()
方法以类似的方式只能将其与承诺的工作,而不是到async.parallel()
。您构建承诺的数组,然后通过他们Promise.all()
:
var listOfPromises = [];
for (var i = 0; i < 10; i++) {
// Construct an array of promises
listOfPromises.push(somePromiseFunction({someParam:i}));
}
Promise.all(listOfPromises).then(function(result){
console.log(result);
});
不要使用Promise.all!如果你的承诺中的任何一个失败,失败,整个操作!
除非你没事与前景,你会过做这样的方式更好:
function sleep(ms) {
return new Promise((resolve, reject) => {
console.log(`starting ${ms}`);
setTimeout(() => {
if (ms > 1000) {
console.log(`Threw out ${ms} because it took too long!`);
reject(ms);
} else {
console.log(`done ${ms}`);
resolve(ms);
}
}, ms);
});
}
(async () => {
console.log('aPromise, bPromise, cPromise executed concurrently as promises are in an array');
const start = new Date();
const aPromise = sleep(2000);
const bPromise = sleep(500);
const cPromise = sleep(5);
try {
const [a, b, c] = [await aPromise, await bPromise, await cPromise];
// The code below this line will only run when all 3 promises are fulfilled:
console.log(`slept well - got ${a} ${b} ${c} in ${new Date()-start}ms`);
} catch (err) {
console.log(`slept rough in ${err}ms`);
}
})();