Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call async/await functions in parallel

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 parallel. So, for example, we have this code:

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?

I want to use it in Node, so maybe there's a solution with async library?

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.

like image 927
Victor Marchuk Avatar asked Feb 24 '16 20:02

Victor Marchuk


People also ask

How do I run async await in parallel?

Use Promise. all for the parallel function calls, the answer behaviors not correctly when the error occurs. 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 after all of the asynchronous calls.

How do you call multiple async functions?

In order to run multiple async/await calls in parallel, all we need to do is add the calls to an array, and then pass that array as an argument to Promise. all() . Promise. all() will wait for all the provided async calls to be resolved before it carries on(see Conclusion for caveat).

Can async be parallel?

Asynchronous operations in parallelThe method async. parallel() is used to run multiple asynchronous operations in parallel. The first argument to async. parallel() is a collection of the asynchronous functions to run (an array, object or other iterable).

Can async method have multiple awaits?

For more information, I have an async / await intro on my blog. So additionally, if a method with multiple awaits is called by a caller, the responsibility for finishing every statement of that method is with the caller.


2 Answers

You can await on 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.

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.

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" }]

Note: If you use Promise.all actions that managed to finish before rejection happen are not rolled back, so you may need to take care of such situation. For example if you have 5 actions, 4 quick, 1 slow and slow rejects. Those 4 actions may be already executed so you may need to roll back. In such situation consider using Promise.allSettled while it will provide exact detail which action failed and which not.

like image 89
madox2 Avatar answered Oct 13 '22 22:10

madox2


TL;DR

Use Promise.all for the parallel function calls, the answer behaviors not correctly when the error occurs.


First, execute all the 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:

// 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

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 after all of the asynchronous calls. See JohnnyHK's comment.


Update: this answer has a different timing in error handling according to the @bergi's answer, it does NOT throw 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

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();  })();
like image 29
Haven Avatar answered Oct 13 '22 23:10

Haven