845

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.

David Ferenczy Rogožan
  • 23,966
  • 9
  • 79
  • 68
Victor Marchuk
  • 13,045
  • 12
  • 43
  • 67
  • Javascript does not run in parallel. You would need to start a new context such as a worker to do that, – Blindman67 Feb 24 '16 at 20:32
  • So where do you want to use it, `async/await` won't be included in ES7 either, but currently is supported in Babel, is that the only place you intend to use this – adeneo Feb 24 '16 at 20:33
  • 1
    @adeneo That is incorrect, Javascript never runs in parallel within its own context. – Blindman67 Feb 24 '16 at 20:42
  • 7
    @Blindman67 - it does, at least the way the OP means, where two async operations are running simultaneously, but not in this case, what I meant to write was that they run in *serial*, the first `await` would wait for the first function to complete entirely before executing the second. – adeneo Feb 24 '16 at 20:46
  • @Bergi that's why something deeply in my unconscious said - it cannot be true. – zerkms Feb 24 '16 at 20:51
  • 1
    Maybe I'm confused, but to run them in parallell, why use `await` at all, if they return promises, why not just use a single `Promise.all` with a `then` handler -> `Promise.all([someCall(), anotherCall()]).then(function(values) {...` – adeneo Feb 24 '16 at 20:52
  • @adeneo No I am sorry but you are mistaken. If it could you would be able to provide an example of how javascript would handle writing to the same variable at the same time. Javascript is not designed to run in parallel because of that very reason. You can not lock javascript variables. – Blindman67 Feb 24 '16 at 20:54
  • 4
    @Blindman67 - it's single threaded, but that limitation doesn't apply to async methods, they **can** run simultaneously, and return the response when they are done, i.e. what the OP means by "parallell". – adeneo Feb 24 '16 at 20:56
  • @adeneo That is not parallel, async objects run native code not javascript nor do any async function get called until the the current execution has ended. All they can do is place calls on the call stack to await sequential execution.. It is misleading to describe javascript and async objects as parallel. The OP ask if two methods could be called in parallel rather than sequentially, This is not possible within a single javascript context, One function must run before the other. – Blindman67 Feb 24 '16 at 21:20
  • 11
    @Blindman67 - I think it's pretty clear what the OP is asking, using the async/await pattern will make the functions run in serial, even if they are async, so the first would completely finish before the second is called etc. The OP is asking how to call both function in parallell, and as they are clearly async, the aim is to run them simultaneously, i.e. in parallell, for instance doing two ajax requests simultaneously, which is not a problem at all in javascript, as most async methods, as you've noted, runs native code, and uses more threads. – adeneo Feb 24 '16 at 21:27
  • @Blindman67: How about "run the two asynchronous tasks concurrently" instead of "call the functions in parallel"? – Bergi Feb 24 '16 at 22:08
  • @Bergi We are not discusing async tasks, but JS functions. To run the two function in "parallel" the initial context must spawn a `ChildProcess` or a `Worker` via `Cluster`, In each cases a new context and JS engine (V8) are instantiated. Depending on the function this may nor be of any benefit. Async tasks can only place calls on the callStack (AKA eventQueue), they can not call JS directly. Any execution in the the current context will block these calls. It is the JS functions the question is concerned about and we should be careful not to use misleading terminology. – Blindman67 Feb 24 '16 at 22:49
  • @Blindman67: A promise-returning asynchronous function is always related to some kind of task for me, and that's what the OP is asking about. Sure, his terminology might not be 100% accurate, but we all understand what he wants to do, and that should suffice. – Bergi Feb 24 '16 at 22:52
  • 4
    @Bergi this is not a duplicate of the linked question — this is specifically about async/await syntax and native `Promise`s. The linked question is regarding the bluebird library with generators & yield. Conceptually similar perhaps, but not in implementation. – Iest May 10 '17 at 15:19
  • @Iest It's conceptually *exactly the same*. The syntax doesn't really matter. – Bergi May 10 '17 at 16:40
  • 1
    @Bergi Callbacks are also doing the same thing as promises and async/await, it's just the syntax that is different. I don't want to use neither callbacks nor generators, though. The question is specifically about async/await syntax. – Victor Marchuk May 11 '17 at 10:50
  • 3
    @Bergi The syntax very much does matter. To a person that has never used generators or bluebird the linked question is completely unhelpful. – Iest May 11 '17 at 17:42

12 Answers12

1350

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.

Lukas Liesis
  • 24,652
  • 10
  • 111
  • 109
madox2
  • 49,493
  • 17
  • 99
  • 99
  • "generator-like syntax:" --- where is it defined? – zerkms Feb 24 '16 at 20:43
  • 1
    @Bergi yep, that's why I asked :-) It was rhetorical :-) – zerkms Feb 24 '16 at 20:50
  • 106
    Clean but be aware the fast-fail behaviour of Promise.all. If any of the functions throws an error, Promise.all will reject – NoNameProvided Feb 08 '17 at 12:26
  • @NoNameProvided so what is the alternative to `Promise.all` in this case? – JohnnyQ Mar 27 '17 at 12:05
  • 13
    You can handle partial results nicely with async/await, see http://stackoverflow.com/a/42158854/2019689 – NoNameProvided Mar 27 '17 at 12:42
  • 159
    Pro tip: use array destructuring in order to initialise an arbitrary number of results from Promise.all(), like: `[result1, result2] = Promise.all([async1(), async2()]);` – jonny Feb 10 '18 at 13:19
  • 13
    @jonny Is this subject to fail-fast? Also, does one still need to `= await Promise.all`? – theUtherSide Jul 12 '18 at 04:51
  • 7
    @theUtherSide You're absolutely right - I neglected to include the await. – jonny Jul 12 '18 at 09:57
  • 6
    @jonny Not to be pedantic, but you'll also need to declare the variables with either `let`, `const`, or `var` as in: `let [result1, result2] = await Promise.all([async1(), async2()]);` – Big Money Dec 13 '18 at 23:17
  • @madox2, does it make any difference if I say "return await Promise.all(...) or just "await Promise.all()"? – MMMM Feb 13 '19 at 08:01
  • @user2774480 in the first case your function will return promise with the result of Promise.all, in second it will return promise but without any result – madox2 Feb 13 '19 at 12:23
  • hello @madox2, why is this? I thought await will resolve (or reject) the promise automatically and then give the result of resolved back (or rejected). Or do I explicitely have to return the value, because otherwise, the value is there, but not used in any way? Can u clarify again when do I need to use "return await Promise.all(...)", when do I need to return it? when I use this Promise.all code in another piece of code? what would the return statement do? – MMMM Feb 27 '19 at 13:08
  • 2
    If you want a one-liner for Promise.all to fail gracefully, use `const results = await Promise.all(promises.map(promise => promise.catch(error => error)));` where `promises` is an array of promises. Since technically everything is resolving now, to check if something failed, iterate through `results` and check if item is an `instanceof Error`. (assuming u reject with an Error). – sudo soul Apr 08 '19 at 20:57
  • @madox2What I'm missing here is that the callback will wait until all promises have resolved, which may not be what you want. E.g. Long and short running process, maybe you don't want the short process callback to execute after the long process has finished. – html_programmer Sep 08 '19 at 14:24
  • To me seems that the same concept of @asyncronous and "parallel" don't fit together completely at all. I mean as long as OP intend to run async code in parallel then this answer is correct, but not considering that the result of those promises will be anyway async, as state here "A pending Promise in all other cases. This returned promise is then resolved/rejected asynchronously (as soon as the stack is empty) when all the promises in the given iterable have resolved, or if any of the promises reject. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all – Carmine Tambascia Aug 05 '21 at 12:37
  • On top as if as soon as one of those Promises passed rejected, then the result also will reject even if all the others would resolve and return the result. For this reason is not really accurate use the term of "Parallel" intending also the result but only limitate that to when they are called – Carmine Tambascia Aug 05 '21 at 12:42
  • Does `Promise.all` actually process all the promises "in parallel" (as best as is possible in JS), i.e. moving between the promises, single threaded, in round robin order every time the current promise hits a blocking operation? Or does it wait for the first promise to resolve before scheduling the second one to resolve, which is what you would happen if you tried to code this up in a `for` loop, `await`-ing each promise's resolution? – Luke Hutchison Jul 22 '22 at 00:53
  • They'd both still need to be asynchronous functions?! – LuckyLuke Skywalker Feb 13 '23 at 14:42
166

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();
})();
Haven
  • 7,808
  • 5
  • 25
  • 37
  • 13
    This looks like a much nicer option to me than Promise.all — and with destructuring assignment you can even do `[someResult, anotherResult] = [await someResult, await anotherResult]` if you change `const` to `let`. – jawj Aug 25 '17 at 13:26
  • 35
    But this still executes the `await` statements serially, right? That is, execution pauses until the first `await` resolves, then moves onto the second. `Promise.all` executes in parallel. – Andru Aug 26 '17 at 11:56
  • 4
    @Andru No, it operates in parallel. I add a jsbin example, you can check there. – Haven Aug 26 '17 at 19:41
  • 1
    I've updated the example with destructuring http://jsbin.com/hutaqehepo/edit?js,console – CKGrafico Aug 30 '17 at 08:07
  • 1
    yay, it looks like object destructuring works too! http://jsbin.com/midefamodu/edit?js,console – Benja Sep 12 '17 at 21:52
  • I tested it, your solution is still slow. – Quang Hoàng Sep 13 '17 at 10:25
  • 1
    This won't work. Each await is executed sequentially. I've verified it in node console. – Henok T Oct 08 '17 at 06:14
  • 99
    This answer is misleading as the fact that both awaits are done in the same line is irrelevant. What matters is that the two async calls are made before either is awaited. – JohnnyHK Oct 16 '17 at 03:09
  • @JohnnyHK Actually it cannot be run in parallel while they are still in a single node application, this is same as `Promise.all` to pretend to run the two async call simultaneously. – Haven Oct 16 '17 at 16:13
  • 20
    @Haven this solution is not the same as `Promise.all`. If each request is a network call, `await someResult` will need to be resolved before `await anotherResult` is even started. Conversely, in `Promise.all` the two `await` calls can be started before either one is resolved. – Ben Winding Feb 07 '18 at 04:59
  • 6
    The answer is misleading. The jsbin code appears to be executing promises in parallel, but they are not. When you create promise with `new` operator, the constructor is called in sync. That's the reason we see `start call starts` & `second call starts` immediately. – Anand N Feb 26 '18 at 07:24
  • Adding to my above comment. The output will still be the same, even if we remove the line `const finalResult = [await someResult, await anotherResult]` – Anand N Feb 26 '18 at 08:08
  • @Anand N It missed a return statement "resolve('ok')" inside both setTimeout function that's why it is still waiting and never return to main. – Lawrence Cheuk Mar 03 '18 at 10:48
  • 2
    Although the question is how to call function in parallel, this answer do give a very good example of how to await both promise to resolve before execute the remaining code. they are not started in parallel but do run concurrently. Thx – Lawrence Cheuk Mar 03 '18 at 11:03
  • 2
    @LawrenceCheuk Yes, Now it's correct :) They are not started Parallel and the Array awaits sequentially Of course the processing has started for all of them (almost at the same time), before the array – Anand N Mar 09 '18 at 10:00
  • Check the below link where it is clearly mentioned that call await in sequence is not replacement for promise.all https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function – Anand N Mar 19 '18 at 11:55
  • 2
    There's a catch to this. If you put the whole statement within a try-catch block and the second promise rejects before the first one is done, the exception would be unhandled. Promise.all is much safer. – habsq Mar 19 '18 at 15:58
  • @habsq I'm not seeing that behavior... http://jsbin.com/hawamihixa/edit?js,console Seems to work fine. – Kaleo Brandt Apr 02 '18 at 18:22
  • @KaleoBrandt check your browser console instead of the jsbin one. This is what I see: `Uncaught (in promise) Error: Whoops! at Promise.then` – habsq Apr 03 '18 at 19:43
  • 1
    This will not make two parallel calls, instead once first await call is done, then next await is executed. Thats the normal behaviour of await, it just waits. Better way would be `const [someResp, anotherResp] = await Promise.all([someResult(), anotherResult()]);` It will make these two calls in parallel and then move on to next line when both responses have come. – Shekhar Kumar May 10 '18 at 07:19
  • 2
    As others have stated, this is not parallel calls, but sequential. – VDog Jul 11 '18 at 21:37
  • 1
    @ShekharKumar That is not true. `results = [await firstPromise, await secondPromise, await thirdPromise]`. In this example, `results` is assigned **_after_** all awaits have resolved and they are being processed since the moment of first execution, before they are being awaited in the array. – Qwerty Nov 19 '18 at 22:40
  • 2
    This appears to work at a cursory glance, but [has horrible problems with unhandled rejections](https://stackoverflow.com/questions/46889290/waiting-for-more-than-one-concurrent-await-operation). **Do not use this!** – Bergi Jan 20 '19 at 19:50
  • 1
    Never use example like in fn `asyncInArray()` because in negative scenario try/catch will not catch errors from all async parallel tasks and you will end with unhandled rejections warning. The only one correct way for async parallel tasks is to use Promise.all() so `asyncInPromiseAll()` in this answer. – mikep Jan 28 '19 at 14:37
127

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:

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;
Jonathan Potter
  • 2,981
  • 1
  • 24
  • 19
  • I would add a comments to the code because there's nothing saying that the next dev is going to understand what you're doing OOB. – Jeff Fischer Jan 07 '18 at 17:44
  • 11
    I feel like this is certainly the most pure answer – Gershom Maes Apr 18 '18 at 15:44
  • 1
    This answer is much more clear than Haven's. It's clear that the function calls will return promise objects, and `await` will then resolve them into actual values. – WSBT Sep 05 '18 at 00:46
  • 6
    This appears to work at a cursory glance, but [has horrible problems with unhandled rejections](https://stackoverflow.com/questions/46889290/waiting-for-more-than-one-concurrent-await-operation). **Do not use this!** – Bergi Jan 20 '19 at 19:51
  • Note that the "Updated answer" is a good way of doing it (using `Promise.all`), unlike the "Original answer", so don't take @Bergi words too strongly. It's indeed the best answer here IMHO. – Vadorequest Mar 23 '19 at 13:22
  • 1
    @Bergi You're right, thanks for pointing that out! I've updated the answer with a better solution. – Jonathan Potter Mar 12 '20 at 14:25
46

There is another way without Promise.all() to do it in parallel:

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
MMMM
  • 3,320
  • 8
  • 43
  • 80
  • 3
    This is dangerous, `promise2` might reject before `promise1` is resolved. If that happens you cannot catch the error from promise1. Either use the sequential pattern in this answer, or use `Promise.all([printNumber1(), printNumber2()])` – oldwizard Nov 18 '20 at 15:11
  • 2
    couldn't you handle the error where the async functions are called? To me, this appears to make it easier to add a `.catch` to each thing individually then the `Promise.all` answers above – Hart Simha May 16 '21 at 12:46
24

I've created a gist testing some different ways of resolving promises, with results. It may be helpful to see the options that work.

Edit: Gist content as per Jin Lee's comment

// Simple gist to test parallel promise resolution when using async / await

function promiseWait(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(true);
    }, time);
});
}


async function test() {
    return [
    await promiseWait(1000),
    await promiseWait(5000),
    await promiseWait(9000),
    await promiseWait(3000),
    ]
}

async function test2() {
    return {
        'aa': await promiseWait(1000),
        'bb': await promiseWait(5000),
        'cc': await promiseWait(9000),
        'dd': await promiseWait(3000),
    }
}

async function test3() {
    return await {
        'aa': promiseWait(1000),
        'bb': promiseWait(5000),
        'cc': promiseWait(9000),
        'dd': promiseWait(3000),
    }
}

async function test4() {
    const p1 =  promiseWait(1000);
    const p2 =  promiseWait(5000);
    const p3 =  promiseWait(9000);
    const p4 =  promiseWait(3000);
    return {
        'aa': await p1,
        'bb': await p2,
        'cc': await p3,
        'dd': await p4,
    };
}

async function test5() {
    return await Promise.all([
                             await promiseWait(1000),
                             await promiseWait(5000),
                             await promiseWait(9000),
                             await promiseWait(3000),
                             ]);
}

async function test6() {
    return await Promise.all([
                             promiseWait(1000),
                             promiseWait(5000),
                             promiseWait(9000),
                             promiseWait(3000),
                             ]);
}

async function test7() {
    const p1 =  promiseWait(1000);
    const p2 =  promiseWait(5000);
    const p3 =  promiseWait(9000);
    return {
        'aa': await p1,
        'bb': await p2,
        'cc': await p3,
        'dd': await promiseWait(3000),
    };
}

let start = Date.now();

test().then((res) => {
    console.log('Test Done, elapsed', (Date.now() - start) / 1000, res);

    start = Date.now();
    test2().then((res) => {
        console.log('Test2 Done, elapsed', (Date.now() - start) / 1000, res);

        start = Date.now();
        test3().then((res) => {
            console.log('Test3 Done, elapsed', (Date.now() - start) / 1000, res);

            start = Date.now();
            test4().then((res) => {
                console.log('Test4 Done, elapsed', (Date.now() - start) / 1000, res);

                start = Date.now();
                test5().then((res) => {
                    console.log('Test5 Done, elapsed', (Date.now() - start) / 1000, res);

                    start = Date.now();
                    test6().then((res) => {
                        console.log('Test6 Done, elapsed', (Date.now() - start) / 1000, res);
                    });

                    start = Date.now();
                    test7().then((res) => {
                        console.log('Test7 Done, elapsed', (Date.now() - start) / 1000, res);
                    });
                });
            });

        });
    });

});
/*
Test Done, elapsed 18.006 [ true, true, true, true ]
Test2 Done, elapsed 18.009 { aa: true, bb: true, cc: true, dd: true }
Test3 Done, elapsed 0 { aa: Promise { <pending> },
  bb: Promise { <pending> },
  cc: Promise { <pending> },
  dd: Promise { <pending> } }
Test4 Done, elapsed 9 { aa: true, bb: true, cc: true, dd: true }
Test5 Done, elapsed 18.008 [ true, true, true, true ]
Test6 Done, elapsed 9.003 [ true, true, true, true ]
Test7 Done, elapsed 12.007 { aa: true, bb: true, cc: true, dd: true }
*/
SkarXa
  • 1,184
  • 1
  • 12
  • 24
  • Tests 4 and 6 in the gist returned the expected results. See https://stackoverflow.com/a/42158854/5683904 by NoNameProvided who explains the difference between the options. – akraines Oct 04 '18 at 15:43
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/28450557) – Jin Lee Mar 03 '21 at 12:42
  • @JinLee Per your suggestion I've added the content, didn't add it originally because it looked like a lot of content – SkarXa Mar 03 '21 at 14:53
  • 1
    @SkarXa SO will like your answer even more now. :) And your code is not that long. Don't worry. Thank you! – Jin Lee Mar 03 '21 at 17:47
23

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

(async function(){
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');
})();
Scott Weaver
  • 7,192
  • 2
  • 31
  • 43
Alex Dresko
  • 5,179
  • 3
  • 37
  • 57
8

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.

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.

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

Thiago Conrado
  • 726
  • 8
  • 15
  • [bluebirds](http://bluebirdjs.com/docs/api-reference.html) .map, .filter and .mapSeries also help with concurrency if you don't want to introduce the async modules callback api – Matt Jul 24 '20 at 01:51
7

You can call multiple asynchronous functions without awaiting them. This will execute them in parallel. While doing so, save the returned promises in variables, and await them at some point either individually or using Promise.all() and process the results.

You can also wrap the function calls with try...catch to handle failures of individual asynchronous actions and provide fallback logic.

Here's an example: Observe the logs, the logs printed at the beginning of execution of the individual asynchronous functions get printed immediately even though the first function takes 5 seconds to resolve.

function someLongFunc () {
    return new Promise((resolve, reject)=> {
        console.log('Executing function 1')
        setTimeout(resolve, 5000)
    })
}

function anotherLongFunc () {
    return new Promise((resolve, reject)=> {
        console.log('Executing function 2')
        setTimeout(resolve, 5000)
    })
}

async function main () {
    let someLongFuncPromise, anotherLongFuncPromise
    const start = Date.now()
    try {
        someLongFuncPromise = someLongFunc()
    }
    catch (ex) {
        console.error('something went wrong during func 1')
    }

    try {
        anotherLongFuncPromise = anotherLongFunc()
    }
    catch (ex) {
        console.error('something went wrong during func 2')
    }

    await someLongFuncPromise
    await anotherLongFuncPromise
    const totalTime = Date.now() - start
    console.log('Execution completed in ', totalTime)
}

main()
Čamo
  • 3,863
  • 13
  • 62
  • 114
4
    // 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.

  • 2
    Welcome to Stack Overflow. While your code may provide the answer to the question, please add context around it so others will have some idea what it does and why it is there. – Theo Jul 02 '19 at 19:27
4

This can be accomplished with Promise.allSettled(), which is similar to Promise.all() but without the fail-fast behavior.

async function Promise1() {
    throw "Failure!";
}

async function Promise2() {
    return "Success!";
}

const [Promise1Result, Promise2Result] = await Promise.allSettled([Promise1(), Promise2()]);

console.log(Promise1Result); // {status: "rejected", reason: "Failure!"}
console.log(Promise2Result); // {status: "fulfilled", value: "Success!"}

Note: This is a bleeding edge feature with limited browser support, so I strongly recommend including a polyfill for this function.

KARTHIKEYAN.A
  • 18,210
  • 6
  • 124
  • 133
-5

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]
}
  • Since you declare the function outside/before the condition test, and called them. Try wrapping them in `else` block. – Haven Sep 29 '17 at 13:21
  • @Haven: I mean when you separate the moments you *call* functions vs *await* can lead to unexpected results, for example: async HTTP requests. – Hoang Le Anh Tu Dec 03 '17 at 03:41
-5

I create a helper function waitAll, may be it can make it sweeter. It only works in nodejs for now, not in browser chrome.

    //const parallel = async (...items) => {
    const waitAll = async (...items) => {
        //this function does start execution the functions
        //the execution has been started before running this code here
        //instead it collects of the result of execution of the functions

        const temp = [];
        for (const item of items) {
            //this is not
            //temp.push(await item())
            //it does wait for the result in series (not in parallel), but
            //it doesn't affect the parallel execution of those functions
            //because they haven started earlier
            temp.push(await item);
        }
        return temp;
    };

    //the async functions are executed in parallel before passed
    //in the waitAll function

    //const finalResult = await waitAll(someResult(), anotherResult());
    //const finalResult = await parallel(someResult(), anotherResult());
    //or
    const [result1, result2] = await waitAll(someResult(), anotherResult());
    //const [result1, result2] = await parallel(someResult(), anotherResult());

Fred Yang
  • 2,521
  • 3
  • 21
  • 29
  • 3
    Nope, parallelization isn't happening at all here. The `for` loop sequentially awaits each promise and adds the result to the array. – Szczepan Hołyszewski Jul 24 '18 at 09:07
  • I understand this seems not working for people. So I tested in node.js and browser. The test is passed in node.js (v10, v11), firefox, it it does not work in browser chrome. The test case is in https://gist.github.com/fredyang/ea736a7b8293edf7a1a25c39c7d2fbbf – Fred Yang Feb 15 '19 at 18:48
  • 2
    I refuse to believe this. There is nothing in the standard that says different iterations of a for loop can be automagically parallelized; this is not how javascript works. The way the loop code is written, it **means** this: "await one item (the await expr), THEN push result to temp, THEN take next item (next iteration of the for loop). The "awaiting" for each item is completely confined to a single iteration of the loop. If tests show that there is parallelization, it must be because the transpiler is doing something nonstandard or is flat out buggy. – Szczepan Hołyszewski Feb 19 '19 at 09:20
  • @SzczepanHołyszewski Your confidence of disbieving without running the test case inspire me to do some rename refactory and extra comments. All code are plain old ES6, no transpiling is required. – Fred Yang Feb 21 '19 at 00:55
  • Not sure why this is downvoted so heavily. It's essentially the same answer that @user2883596 gave. – Jonathan Sudiaman Apr 06 '20 at 15:19