Javascript 并行调用 async/await 函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35612428/
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
Call async/await functions in parallel
提问by Victor Marchuk
As far as I understand, in ES7/ES2016 putting multiple await
's in code will work similar to chaining .then()
with promises, meaning that they will execute one after the other rather than in parallerl. So, for example, we have this code:
据我了解,在 ES7/ES2016 中,await
在代码中放置多个's 将类似于.then()
使用 promise链接,这意味着它们将一个接一个地执行,而不是并行执行。因此,例如,我们有以下代码:
await someCall();
await anotherCall();
Do I understand it correctly that anotherCall()
will be called only when someCall()
is completed? What is the most elegant way of calling them in parallel?
我是否正确理解它anotherCall()
只有在someCall()
完成时才会被调用?并行调用它们的最优雅方式是什么?
I want to use it in Node, so maybe there's a solution with async library?
我想在 Node 中使用它,所以也许有一个带有异步库的解决方案?
EDIT: I'm not satisfied with the solution provided in this question: Slowdown due to non-parallel awaiting of promises in async generators, because it uses generators and I'm asking about a more general use case.
编辑:我对这个问题中提供的解决方案不满意:Slowdown due to non-parallel awaits in async generators,因为它使用生成器,我问的是一个更一般的用例。
回答by madox2
You can await on Promise.all()
:
您可以等待Promise.all()
:
await Promise.all([someCall(), anotherCall()]);
To store the results:
存储结果:
let [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
Note that Promise.all
fails fast, which means that as soon as one of the promises supplied to it rejects, then the entire thing rejects.
请注意,Promise.all
失败很快,这意味着一旦提供给它的承诺之一被拒绝,整个事情就会被拒绝。
const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))
Promise.all([happy('happy', 100), sad('sad', 50)])
.then(console.log).catch(console.log) // 'sad'
If, instead, you want to wait for all the promises to either fulfill or reject, then you can use Promise.allSettled
. Note that Internet Explorer does not natively support this method.
相反,如果您想等待所有承诺履行或拒绝,那么您可以使用Promise.allSettled
. 请注意,Internet Explorer 本身不支持此方法。
const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))
Promise.allSettled([happy('happy', 100), sad('sad', 50)])
.then(console.log) // [{ "status":"fulfilled", "value":"happy" }, { "status":"rejected", "reason":"sad" }]
回答by Haven
TL;DR
TL; 博士
Use Promise.all
for the parallel function calls, the answer behaviors not correctly when the error occurs.
使用Promise.all
的并行函数调用,答案的行为不正确时出现错误。
First, execute allthe asynchronous calls at once and obtain all the Promise
objects. Second, use await
on the Promise
objects. This way, while you wait for the first Promise
to resolve the other asynchronous calls are still progressing. Overall, you will only wait for as long as the slowest asynchronous call. For example:
首先,一次执行所有异步调用并获取所有Promise
对象。第二,await
在Promise
对象上使用。这样,在您等待第一个Promise
解决的同时,其他异步调用仍在进行中。总的来说,您只会等待最慢的异步调用。例如:
// Begin first call and store promise without waiting
const someResult = someCall();
// Begin second call and store promise without waiting
const anotherResult = anotherCall();
// Now we await for both results, whose async processes have already been started
const finalResult = [await someResult, await anotherResult];
// At this point all calls have been resolved
// Now when accessing someResult| anotherResult,
// you will have a value instead of a promise
JSbin example: http://jsbin.com/xerifanima/edit?js,console
JSbin 示例:http://jsbin.com/xerifanima/edit?js,console
Caveat:It doesn't matter if the await
calls are on the same line or on different lines, so long as the first await
call happens afterall of the asynchronous calls. See JohnnyHK's comment.
警告:await
调用是在同一行还是在不同的行上都没有关系,只要第一个await
调用发生在所有异步调用之后即可。请参阅 JohnnyHK 的评论。
Update:this answer has a different timing in error handling according to the @bergi's answer, it does NOTthrow out the error as the error occurs but after all the promises are executed.
I compare the result with @jonny's tip: [result1, result2] = Promise.all([async1(), async2()])
, check the following code snippet
更新:这个回答有错误不同时间处理根据@ BERGI的答案,但它不是为发生错误扔出去的错误,但所有的承诺都执行之后。我将结果与@jonny 的提示进行比较:[result1, result2] = Promise.all([async1(), async2()])
,检查以下代码片段
const correctAsync500ms = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, 'correct500msResult');
});
};
const correctAsync100ms = () => {
return new Promise(resolve => {
setTimeout(resolve, 100, 'correct100msResult');
});
};
const rejectAsync100ms = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, 'reject100msError');
});
};
const asyncInArray = async (fun1, fun2) => {
const label = 'test async functions in array';
try {
console.time(label);
const p1 = fun1();
const p2 = fun2();
const result = [await p1, await p2];
console.timeEnd(label);
} catch (e) {
console.error('error is', e);
console.timeEnd(label);
}
};
const asyncInPromiseAll = async (fun1, fun2) => {
const label = 'test async functions with Promise.all';
try {
console.time(label);
let [value1, value2] = await Promise.all([fun1(), fun2()]);
console.timeEnd(label);
} catch (e) {
console.error('error is', e);
console.timeEnd(label);
}
};
(async () => {
console.group('async functions without error');
console.log('async functions without error: start')
await asyncInArray(correctAsync500ms, correctAsync100ms);
await asyncInPromiseAll(correctAsync500ms, correctAsync100ms);
console.groupEnd();
console.group('async functions with error');
console.log('async functions with error: start')
await asyncInArray(correctAsync500ms, rejectAsync100ms);
await asyncInPromiseAll(correctAsync500ms, rejectAsync100ms);
console.groupEnd();
})();
回答by Jonathan Potter
Update:
更新:
The original answer makes it difficult (and in some cases impossible) to correctly handle promise rejections. The correct solution is to use Promise.all
:
原始答案使得正确处理承诺拒绝变得困难(在某些情况下是不可能的)。正确的解决方案是使用Promise.all
:
const [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
Original answer:
原答案:
Just make sure you call both functions before you await either one:
只需确保在等待任一函数之前调用这两个函数:
// Call both functions
const somePromise = someCall();
const anotherPromise = anotherCall();
// Await both promises
const someResult = await somePromise;
const anotherResult = await anotherPromise;
回答by user2883596
There is another way without Promise.all() to do it in parallel:
还有另一种没有 Promise.all() 的方法可以并行执行:
First, we have 2 functions to print numbers:
首先,我们有两个函数来打印数字:
function printNumber1() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log("Number1 is done");
resolve(10);
},1000);
});
}
function printNumber2() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log("Number2 is done");
resolve(20);
},500);
});
}
This is sequential:
这是顺序的:
async function oneByOne() {
const number1 = await printNumber1();
const number2 = await printNumber2();
}
//Output: Number1 is done, Number2 is done
This is parallel:
这是平行的:
async function inParallel() {
const promise1 = printNumber1();
const promise2 = printNumber2();
const number1 = await promise1;
const number2 = await promise2;
}
//Output: Number2 is done, Number1 is done
回答by Jonathan Sudiaman
This can be accomplished with Promise.allSettled(), which is similar to Promise.all()
but without the fail-fast behavior.
这可以通过Promise.allSettled()来完成,它类似于Promise.all()
但没有快速失败的行为。
async function failure() {
throw "Failure!";
}
async function success() {
return "Success!";
}
const [failureResult, successResult] = await Promise.allSettled([failure(), success()]);
console.log(failureResult); // {status: "rejected", reason: "Failure!"}
console.log(successResult); // {status: "fulfilled", value: "Success!"}
Note: This is a bleeding edge feature with limited browser support, so I stronglyrecommend including a polyfill for this function.
注意:这是一项具有有限浏览器支持的前沿功能,因此我强烈建议为此功能包含一个 polyfill。
回答by SkarXa
回答by Thrunobulax
// A generic test function that can be configured
// with an arbitrary delay and to either resolve or reject
const test = (delay, resolveSuccessfully) => new Promise((resolve, reject) => setTimeout(() => {
console.log(`Done ${ delay }`);
resolveSuccessfully ? resolve(`Resolved ${ delay }`) : reject(`Reject ${ delay }`)
}, delay));
// Our async handler function
const handler = async () => {
// Promise 1 runs first, but resolves last
const p1 = test(10000, true);
// Promise 2 run second, and also resolves
const p2 = test(5000, true);
// Promise 3 runs last, but completes first (with a rejection)
// Note the catch to trap the error immediately
const p3 = test(1000, false).catch(e => console.log(e));
// Await all in parallel
const r = await Promise.all([p1, p2, p3]);
// Display the results
console.log(r);
};
// Run the handler
handler();
/*
Done 1000
Reject 1000
Done 5000
Done 10000
*/
Whilst setting p1, p2 and p3 is not strictly running them in parallel, they do not hold up any execution and you can trap contextual errors with a catch.
虽然设置 p1、p2 和 p3 并不是严格并行运行它们,但它们不会阻止任何执行,您可以使用 catch 捕获上下文错误。
回答by Alex Dresko
In my case, I have several tasks I want to execute in parallel, but I need to do something different with the result of those tasks.
就我而言,我有几个要并行执行的任务,但我需要对这些任务的结果做一些不同的事情。
function wait(ms, data) {
console.log('Starting task:', data, ms);
return new Promise(resolve => setTimeout(resolve, ms, data));
}
var tasks = [
async () => {
var result = await wait(1000, 'moose');
// do something with result
console.log(result);
},
async () => {
var result = await wait(500, 'taco');
// do something with result
console.log(result);
},
async () => {
var result = await wait(5000, 'burp');
// do something with result
console.log(result);
}
]
await Promise.all(tasks.map(p => p()));
console.log('done');
And the output:
和输出:
Starting task: moose 1000
Starting task: taco 500
Starting task: burp 5000
taco
moose
burp
done
回答by Thiago Conrado
await Promise.all([someCall(), anotherCall()]); as already mention will act as a thread fence (very common in parallel code as CUDA), hence it will allow all the promises in it to run without blocking each other, but will prevent the execution to continue until ALL are resolved.
等待 Promise.all([someCall(), anotherCall()]); 如前所述,它将充当线程栅栏(在作为 CUDA 的并行代码中非常常见),因此它将允许其中的所有 promise 运行而不会相互阻塞,但会阻止执行继续,直到解决所有问题。
another approach that is worth to share is the Node.js async that will also allow you to easily control the amount of concurrency that is usually desirable if the task is directly linked to the use of limited resources as API call, I/O operations, etc.
另一种值得分享的方法是 Node.js 异步,如果任务直接链接到有限资源的使用,如 API 调用、I/O 操作,它也将允许您轻松控制通常需要的并发量,等等。
// create a queue object with concurrency 2
var q = async.queue(function(task, callback) {
console.log('Hello ' + task.name);
callback();
}, 2);
// assign a callback
q.drain = function() {
console.log('All items have been processed');
};
// add some items to the queue
q.push({name: 'foo'}, function(err) {
console.log('Finished processing foo');
});
q.push({name: 'bar'}, function (err) {
console.log('Finished processing bar');
});
// add some items to the queue (batch-wise)
q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function(err) {
console.log('Finished processing item');
});
// add some items to the front of the queue
q.unshift({name: 'bar'}, function (err) {
console.log('Finished processing bar');
});
Credits to the Medium article autor (read more)
归功于 Medium 文章作者(阅读更多)
回答by Hoang Le Anh Tu
I vote for:
我投票赞成:
await Promise.all([someCall(), anotherCall()]);
Be aware of the moment you call functions, it may cause unexpected result:
注意调用函数的那一刻,可能会导致意想不到的结果:
// Supposing anotherCall() will trigger a request to create a new User
if (callFirst) {
await someCall();
} else {
await Promise.all([someCall(), anotherCall()]); // --> create new User here
}
But following always triggers request to create new User
但是以下总是触发创建新用户的请求
// Supposing anotherCall() will trigger a request to create a new User
const someResult = someCall();
const anotherResult = anotherCall(); // ->> This always creates new User
if (callFirst) {
await someCall();
} else {
const finalResult = [await someResult, await anotherResult]
}