Concurrent start with async/await - javascript

I thought I had a pretty good understanding of async await until I tried this:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('what'), 10000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('?'), 15000);
});
async function asyncTest() {
console.time();
const p1 = await promise1;
if (p1 === 'what') {
var p2 = await promise2;
}
console.log(p1, p2);
console.timeEnd();
}
asyncTest()
After 15000ms, asyncTest is logging p1 & p2. If instead promise1 and promise2 are transformed into functions that return these promises, execution time is then 25000ms. I have no idea what's going on. Could anyone care to explain this?

The issues is that your Promise callbacks are invoked immediately after you declare them.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('what'), 10000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('?'), 15000);
});
At this point, both callbacks have been invoked. 10000ms from this point, promise1 will resolve. 15000ms from this point, promise2 will resolve.
async function asyncTest() {
console.time();
const p1 = await promise1;
if (p1 === 'what') {
var p2 = await promise2;
}
console.log(p1, p2);
console.timeEnd();
}
asyncTest()
At this point, as both callbacks have already been invoked, both will be resolved within 15000ms, which is what you observed.
In order to stretch this to the 25000ms you were expecting, try rewriting the Promises as follows:
const promise1 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('what'), 10000);
});
const promise2 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('?'), 15000);
});
Note these are now function expressions that will not be invoked immediately. Then rewrite your asyncTest function to invoke these expressions instead of simply await the already executing Promises:
async function asyncTest() {
console.time();
const p1 = await promise1();
if (p1 === 'what') {
var p2 = await promise2();
}
console.log(p1, p2);
console.timeEnd();
}
This will force promise2 to wait until promise1 is completed before beginning execution.

With the code in its current state:
You run setTimeout and assign a promise that will handle its result to promise1. Immediately after, you do the same thing for promise2.
So promise2 resolves about five seconds after promise1.
If you change the code so that promise1 and promise2 are functions, then:
You run setTimeout return a promise that will handle its result to await. It will wait until the promise resolves 10 seconds later. Then it will assign the result to p1.
Then (i.e. after 10 seconds have passed instead of immediately) it will do the same thing for p2.
In short:
In the first example, you don't wait for promise1 before setting promise2 going.
After making the change you describe, you do await promise1 before setting promise2 going.

In the first example, two promises fire-off instantly at the beginning, and by the time when browser executes line var p2 = await promise2; the promise2 is on-half way to resolve and await statement pauses for 5000 ms.
console.log('promise1 created');
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('what'), 10000);
});
console.log('promise2 created');
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('?'), 15000);
});
async function asyncTest() {
console.time();
const p1 = await promise1;
if (p1 === 'what') {
var p2 = await promise2;
}
console.log(p1, p2);
console.timeEnd();
}
asyncTest();
In the second example, you fire promises only when browser reaches the line where the function firing promise is called. So at line var p2 = await promise2(); the second promise is created and await statement pauses for 15000 ms. Thus total execution time is 25000 ms.
const promise1 = () => {
console.log('promise1 created');
return new Promise((resolve, reject) => {
setTimeout(() => resolve('what'), 10000);
})
};
const promise2 = () => {
console.log('promise2 created');
return new Promise((resolve, reject) => {
setTimeout(() => resolve('?'), 15000);
})
};
async function asyncTest() {
console.time();
const p1 = await promise1();
if (p1 === 'what') {
var p2 = await promise2();
}
console.log(p1, p2);
console.timeEnd();
}
asyncTest();

Related

Why do async await and Promise.all have the same running time?

I have created three Promises as follows and awaited them, expecting them to take 6000 milliseconds:
let PromiseOne = new Promise(resolve => {
setTimeout(() => {
resolve('Promise One');
}, 2000);
});
let PromiseTwo = new Promise(resolve => {
setTimeout(() => {
resolve('Promise Two');
}, 2000);
});
let PromiseThree = new Promise(resolve => {
setTimeout(() => {
resolve('Promise Three');
}, 2000);
});
(async() => {
console.time();
let ResponseOne = await PromiseOne;
let ResponseTwo = await PromiseTwo;
let ResponseThree = await PromiseThree;
console.log(ResponseOne, ResponseTwo, ResponseThree);
console.timeEnd();
})();
But, I have received an unexpected console message as follows.
Promise One Promise Two Promise Three
default: 2.004s
As I know, each await has 2000ms of running time. But, It doesn't.
In this case, what is the difference between my code above using async/await and the code below using Promise.all?
let PromiseOne = new Promise(resolve => {
setTimeout(() => {
resolve('Promise One');
}, 2000);
});
let PromiseTwo = new Promise(resolve => {
setTimeout(() => {
resolve('Promise Two');
}, 2000);
});
let PromiseThree = new Promise(resolve => {
setTimeout(() => {
resolve('Promise Three');
}, 2000);
});
(() => {
Promise.all([PromiseOne, PromiseTwo, PromiseThree]).then(res => {
console.log(res);
});
})();
In both cases, you're constructing all the Promises immediately. After the declaration of PromiseThree ends, for both, you're left with 3 promises, each of which resolves after 2 seconds. No matter what happens later in the script, each of those will resolve after 2 seconds.
If you use Promise.all, all of the promises will have resolved after 2 seconds.
If you use async/await, the first one you wait for will take 2 seconds to resolve. At that same time, the other two will have resolved, so doing await PromiseTwo and await PromiseThree will take next to no time at all - so, still a total of 2 seconds.
// at this point, each promise has been created and each will take 2 seconds
(async() => {
console.time();
// 2 seconds for the below line
let ResponseOne = await PromiseOne;
// after 2 seconds, all have resolved; below line takes no time at all
let ResponseTwo = await PromiseTwo;
// same as above
let ResponseThree = await PromiseOne;
It'd be different if you constructed the other promises after awaiting the previous ones - in which case they'd run in serial as you're expecting with await.
let makePromise = () => new Promise(resolve => {
setTimeout(() => {
resolve();
}, 2000);
});
console.log('starting');
(async () => {
console.time();
await makePromise();
await makePromise();
await makePromise();
console.timeEnd();
})();

Await keyword in Javascript async function is not waiting for promises to complete in sequential order

I am new to Javascript and trying to learn promises and async/await concepts. I created three promises as shown below.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise1");
resolve("1");
}, 1000)
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise2");
resolve("2");
}, 5000)
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise3");
resolve("3");
}, 4000)
});
I have created a async function which is using await for multiple promises sequentially as shown below.
async function example() {
const result1 = await promise1;
const result2 = await promise2;
const result3 = await promise3;
console.log(result1);
console.log(result2);
console.log(result3);
}
example();
The output in browser console is -
promise1
promise3
promise2
1
2
3
I am not understanding why in my output promise3 is coming before promise2 because in sequence of await statements inside async function example, promise2 comes before promise3? According to me the output should be like below -
promise1
promise2
promise3
1
2
3
Please correct me if I am missing something or if there is any error.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise1");
resolve("1");
}, 1000)
});
When you construct a promise, the code inside it runs immediately. So as soon as this line is done, a timer is off and running. Your code creates the 3 of these promises up front, which means it starts all 3 timers up front.
Later on when you await the promise, that does not change what the timers are doing. It just lets your async function know when they are done. So in the background, the timers will start going off, logging things out, and resolving their corresponding promises. This can happen even if nothing is awaiting the promise. The timeout of 1000ms will be the first to go off, followed 3 seconds later by the one of 4000ms, and then 1 second later the one of 5000ms
If you want the timers to only start once you get to that line of the async function, then you need to do the setTimeouts at that line of the async function. For example:
async function example() {
const result1 = await new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise1");
resolve("1");
}, 1000);
});
const result2 = await new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise2");
resolve("2");
}, 5000);
});
const result3 = await new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise3");
resolve("3");
}, 4000);
});
console.log(result1);
console.log(result2);
console.log(result3);
}
In a nutshell, it's because you first create those three promises and in the process of creating those three promises, you also start your three timers. Those three timers are all started and are all running in parallel.
Then, you do await promise1. But, that statement has nothing at all to do with when the timers call their callback. They will do that entirely upon their own. Thus, the timer callbacks themselves create console.log() output according to the expected time each timer is set for (nothing at all to do with the await you have).
So, it happens this way because your first create all three promises and timers and THEN and only then, do you do an await. An important thing to understand here is that "promises don't execute". They aren't the asynchronous operation themselves. As soon as you do new Promise(), it calls the promise executor function and the asynchronous operation you have in there is started. What the promise does from then on is monitor that asynchronous operation and then notify observers when that asynchronous operation completes.
You can see more detail on the order of things if you add logging for when each timer is started which will show that all three timers are initially started and running in parallel and can call their timer callbacks completely independent of your await statements later in the code:
const promise1 = new Promise((resolve, reject) => {
console.log("starting timer 1");
setTimeout(() => {
console.log("promise1");
resolve("1");
}, 1000)
});
const promise2 = new Promise((resolve, reject) => {
console.log("starting timer 2");
setTimeout(() => {
console.log("promise2");
resolve("2");
}, 5000)
});
const promise3 = new Promise((resolve, reject) => {
console.log("starting timer 3");
setTimeout(() => {
console.log("promise3");
resolve("3");
}, 4000)
});
async function example() {
const result1 = await promise1;
const result2 = await promise2;
const result3 = await promise3;
console.log(result1);
console.log(result2);
console.log(result3);
}
example();
If you changed the structure of your code so that a function you were awaiting is what actually creates and starts the timers, then you wouldn't be starting the second timer until after the first timer fires and you'd have sequential timers.
So, your expected output would happen if you did this instead:
function runTimer1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise1");
resolve("1");
}, 1000)
});
}
function runTimer2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise2");
resolve("2");
}, 5000)
});
}
function runTimer3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise3");
resolve("3");
}, 4000)
});
}
async function example() {
const result1 = await runTimer1();
const result2 = await runTimer2();
const result3 = await runTimer3();
console.log(result1);
console.log(result2);
console.log(result3);
}
example();
That's because you're creating the Promises outside of the async function without awaiting. The callback inside the Promise is syncrounos. You run the 3 setTimeout's one after another. Example:
const x = new Promise(resolve => console.log(1))
console.log(2)
This logs out 1,2 - as opposed to asyncrounous callbacks ex. in setTimeout's:
setTimeout(() => console.log(1));
console.log(2)
This logs out 2,1.
So, if you want the correct behaviour you'd have to create the Promises when you await for them, and that's common practice:
function makePromise(time, number) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise", number);
resolve(number);
}, time);
});
}
async function example() {
const result1 = await makePromise(1000, 1);
const result2 = await makePromise(5000, 2);
const result3 = await makePromise(4000, 3);
console.log(result1);
console.log(result2);
console.log(result3);
}
example();

Strange behaviour of asynchtonous function with rejected promise [duplicate]

How can I change the following code so that both async operations are triggered and given an opportunity to run concurrently?
const value1 = await getValue1Async();
const value2 = await getValue2Async();
// use both values
Do I need to do something like this?
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
// use both values
TL;DR
Don't use the pattern in the question where you get the promises, and then separately wait on them; instead, use Promise.all (at least for now):
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
While your solution does run the two operations in parallel, it doesn't handle rejection properly if both promises reject.
Details:
Your solution runs them in parallel, but always waits for the first to finish before waiting for the second. If you just want to start them, run them in parallel, and get both results, it's just fine. (No, it isn't, keep reading...) Note that if the first takes (say) five seconds to complete and the second fails in one second, your code will wait the full five seconds before then failing.
Sadly, there isn't currently await syntax to do a parallel wait, so you have the awkwardness you listed, or Promise.all. (There's been discussion of await.all or similar, though; maybe someday.)
The Promise.all version is:
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
...which is more concise, and also doesn't wait for the first operation to complete if the second fails quickly (e.g., in my five seconds / one second example above, the above will reject in one second rather than waiting five). Also note that with your original code, if the second promise rejects before the first promise resolves, you may well get a "unhandled rejection" error in the console (you do currently with Chrome v61; update: more recent versions have more interesting behavior), although that error is arguably spurious (because you do, eventually, handle the rejection, in that this code is clearly in an async function¹ and so that function will hook rejection and make its promise reject with it) (update: again, changed). But if both promises reject, you'll get a genuine unhandled rejection error because the flow of control never reaches const value2 = await p2; and thus the p2 rejection is never handled.
Unhandled rejections are a Bad Thing™ (so much so that soon, Node.js will abort the process on truly unhandled rejections, just like unhandled exceptions — because that's what they are), so best to avoid the "get the promise then await it" pattern in your question.
Here's an example of the difference in timing in the failure case (using 500ms and 100ms rather than 5 seconds and 1 second), and possibly also the arguably-spurious unhandled rejection error (open the real browser console to see it):
const getValue1Async = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, "value1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error");
});
};
// This waits the full 500ms before failing, because it waits
// on p1, then on p2
(async () => {
try {
console.time("separate");
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
} catch (e) {
console.error(e);
}
console.timeEnd("separate");
})();
// This fails after just 100ms, because it doesn't wait for p1
// to finish first, it rejects as soon as p2 rejects
setTimeout(async () => {
try {
console.time("Promise.all");
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
} catch (e) {
console.timeEnd("Promise.all", e);
}
}, 1000);
Open the real browser console to see the unhandled rejection error.
And here we reject both p1 and p2, resulting in a non-spurious unhandled rejection error on p2:
const getValue1Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 500, "error1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error2");
});
};
// This waits the full 500ms before failing, because it waits
// on p1, then on p2
(async () => {
try {
console.time("separate");
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
} catch (e) {
console.error(e);
}
console.timeEnd("separate");
})();
// This fails after just 100ms, because it doesn't wait for p1
// to finish first, it rejects as soon as p2 rejects
setTimeout(async () => {
try {
console.time("Promise.all");
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
} catch (e) {
console.timeEnd("Promise.all", e);
}
}, 1000);
Open the real browser console to see the unhandled rejection error.
In a comment you've asked:
Side question: will the following force waiting for both (and discarding the results) await p1 && await p2?
This has the same issues around promise rejection as your original code: It will wait until p1 resolves even if p2 rejects earlier; it may generate an arguably-spurious (update: or temporary) unhandled rejection error if p2 rejects before p1 resolves; and it generates a genuine unhandled rejection error if both p1 and p2 reject (because p2's rejection is never handled).
Here's the case where p1 resolves and p2 rejects:
const getValue1Async = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, false);
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error");
});
};
(async () => {
try {
const p1 = getValue1Async();
const p2 = getValue2Async();
console.log("waiting");
await p1 && await p2;
} catch (e) {
console.error(e);
}
console.log("done waiting");
})();
Look in the real console (for the unhandled rejection error).
...and where both reject:
const getValue1Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 500, "error1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error2");
});
};
(async () => {
try {
const p1 = getValue1Async();
const p2 = getValue2Async();
console.log("waiting");
await p1 && await p2;
} catch (e) {
console.error(e);
}
console.log("done waiting");
})();
Look in the real console (for the unhandled rejection error).
¹ "...this code is clearly in an async function..." That was true in 2017 when this question and answer were written. Since then, top-level await happened/is happening.
I think this should work:
const [value1, value2] = await Promise.all([getValue1Async(),getValue2Async()]);
A more verbose example is below in case it helps in understanding:
const promise1 = async() => {
return 3;
}
const promise2 = async() => {
return 42;
}
const promise3 = async() => {
return 500;
// emulate an error
// throw "something went wrong...";
}
const f1 = async() => {
try {
// returns an array of values
const results = await Promise.all([promise1(), promise2(), promise3()]);
console.log(results);
console.log(results[0]);
console.log(results[1]);
console.log(results[2]);
// assigns values to individual variables through 'array destructuring'
const [value1, value2, value3] = await Promise.all([promise1(), promise2(), promise3()]);
console.log(value1);
console.log(value2);
console.log(value3);
} catch (err) {
console.log("there was an error: " + err);
}
}
f1();
Use .catch() and Promise.all()
Make sure you handle rejections correctly and you can safely use Promises.all() without facing unhandled rejections. (Edit: clarification per discussion: not the Error unhandled rejection but simply rejections that are not being handled by the code. Promise.all() will throw the first promise rejection and will ignore the rest).
In the example below an array of [[error, results], ...] is returned to allow ease of processing results and/or errors.
let myTimeout = (ms, is_ok) =>
new Promise((resolve, reject) =>
setTimeout(_=> is_ok ?
resolve(`ok in ${ms}`) :
reject(`error in ${ms}`),
ms));
let handleRejection = promise => promise
.then((...r) => [null, ...r])
.catch(e => [e]);
(async _=> {
let res = await Promise.all([
myTimeout(100, true),
myTimeout(200, false),
myTimeout(300, true),
myTimeout(400, false)
].map(handleRejection));
console.log(res);
})();
You may throw from within a catch() to stop waiting for all (and discard the results of the rest), however - you may only do it once per try/catch blocks so a flag has_thorwn need to be maintained and checked to make sure no unhandled errors happens.
let myTimeout = (ms, is_ok) =>
new Promise((resolve, reject) =>
setTimeout(_=> is_ok ?
resolve(`ok in ${ms}`) :
reject(`error in ${ms}`),
ms));
let has_thrown = false;
let handleRejection = promise => promise
.then((...r) => [null, ...r])
.catch(e => {
if (has_thrown) {
console.log('not throwing', e);
} else {
has_thrown = 1;
throw e;
}
});
(async _=> {
try {
let res = await Promise.all([
myTimeout(100, true),
myTimeout(200, false),
myTimeout(300, true),
myTimeout(400, false)
].map(handleRejection));
console.log(res);
} catch(e) {
console.log(e);
}
console.log('we are done');
})();
Resolves instead of Promises
const wait = (ms, data) => new Promise( resolve => setTimeout(resolve, ms, data) )
const reject = (ms, data) => new Promise( (r, reject) => setTimeout(reject, ms, data) )
const e = e => 'err:' + e
const l = l => (console.log(l), l)
;(async function parallel() {
let task1 = reject(500, 'parallelTask1').catch(e).then(l)
let task2 = wait(2500, 'parallelTask2').catch(e).then(l)
let task3 = reject(1500, 'parallelTask3').catch(e).then(l)
console.log('WAITING')
;[task1, task2, task3] = [await task1, await task2, await task3]
console.log('FINISHED', task1, task2, task3)
})()
As was pointed out in other answers, a rejected promise might raise an unhandled exception.
This one .catch(e => e) is a neat little trick that catches the error and passes it down the chain, allowing the promise to resolve, instead of rejecting.
If you find this ES6 code ugly see friendlier here.

Putting up delay in promises

So, I have two promises that I want to print on screen with a 3sec delay in between. How would I achieve it. Below is the code.
const promiseOne = new Promise(function(resolve, reject) {
resolve("Hello")
});
const promiseTwo = new Promise(function(resolve, reject) {
resolve("Good Morning")
});
Promise.all([promiseOne, promiseTwo]).then(function() {
setTimeout(() => {
const response = Promise.all.next();
console.log(response);
}, 3000);
});
I suggest you use a delay function. First you loop through each response in the array and use the delay in between
const promiseOne = new Promise(function(resolve, reject) {
resolve("Hello")
});
const promiseTwo = new Promise(function(resolve, reject) {
resolve("Good Morning")
});
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Promise.all([promiseOne, promiseTwo]).then(async function(resps) {
for(let res of resps){
console.log(res);
await delay(3000)
}
});
const promiseOne = new Promise(function(resolve, reject) {
resolve("Hello")
});
const promiseTwo = new Promise(function(resolve, reject) {
resolve("Good Morning")
});
Promise.all([promiseOne, promiseTwo]).then(function(all) {
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(all);
}
, 3000);
});
}).then(console.log)
Return new Promise in then, that promise waits for x millisecond and resolve it.
And do anything in next then
If you just want to delay the log then you can add response parameter as explained by #Daniel Rodríguez Meza.
But if you want to delay response from any one of the promise promiseOne or promiseTwo then you should use setTimeout(() => resolve("Hello"), 300); inside respective promise as shown below. Also do not use setTimeout inside Promise.All.
As per OP's comment I have updated answer to resolve promiseTwo 3 seconds after promiseOne resolves.
Here I've assigned resolve of promiseTwo to global variable resolvePromiseTwo which is used inside promiseOne to resolve promiseTwo after 3 seconds.
Note I have used .then after promiseOne and promiseTwo just to verify output. You can omit both.
let resolvePromiseTwo = null;
const promiseOne = new Promise(function(resolve, reject) {
resolve("Good Morning")
setTimeout(() => resolvePromiseTwo("Hello"), 3000);
}).then(res => {
console.log('promiseOne resolved');
return res;
});
const promiseTwo = new Promise(function(resolve, reject) {
resolvePromiseTwo = resolve;
}).then(res => {
console.log('promiseTwo resolved');
return res;
});
Promise.all([promiseOne, promiseTwo]).then(function(response) {
console.log('Promise.all');
console.log(response);
});
You were pretty close, you just need to receive the value from the results of the Promise.all and then handle that information, like this:
const promiseOne = new Promise(function(resolve) {
resolve("Hello")
});
const promiseTwo = new Promise(function(resolve) {
resolve("Good Morning");
});
Promise.all([promiseOne, promiseTwo])
.then(function(response) {
setTimeout(() => {
console.log(response);
}, 3000);
});
Edit
According to the clarification given by OP what he needs is the following:
After the first promise is resolved, wait 3 seconds and then execute the second promise.
const promiseOne = new Promise(function(resolve) {
resolve("Hello")
});
const promiseTwo = new Promise(function(resolve) {
resolve("Good Morning");
});
async function resolveWithDelay(delay = 3000) {
const res1 = await promiseOne;
console.log(res1);
setTimeout(async () => {
const res2 = await promiseTwo;
console.log(res2);
}, delay);
}
resolveWithDelay();

Resolving multiple promise variables in parallel using async/await [duplicate]

How can I change the following code so that both async operations are triggered and given an opportunity to run concurrently?
const value1 = await getValue1Async();
const value2 = await getValue2Async();
// use both values
Do I need to do something like this?
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
// use both values
TL;DR
Don't use the pattern in the question where you get the promises, and then separately wait on them; instead, use Promise.all (at least for now):
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
While your solution does run the two operations in parallel, it doesn't handle rejection properly if both promises reject.
Details:
Your solution runs them in parallel, but always waits for the first to finish before waiting for the second. If you just want to start them, run them in parallel, and get both results, it's just fine. (No, it isn't, keep reading...) Note that if the first takes (say) five seconds to complete and the second fails in one second, your code will wait the full five seconds before then failing.
Sadly, there isn't currently await syntax to do a parallel wait, so you have the awkwardness you listed, or Promise.all. (There's been discussion of await.all or similar, though; maybe someday.)
The Promise.all version is:
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
...which is more concise, and also doesn't wait for the first operation to complete if the second fails quickly (e.g., in my five seconds / one second example above, the above will reject in one second rather than waiting five). Also note that with your original code, if the second promise rejects before the first promise resolves, you may well get a "unhandled rejection" error in the console (you do currently with Chrome v61; update: more recent versions have more interesting behavior), although that error is arguably spurious (because you do, eventually, handle the rejection, in that this code is clearly in an async function¹ and so that function will hook rejection and make its promise reject with it) (update: again, changed). But if both promises reject, you'll get a genuine unhandled rejection error because the flow of control never reaches const value2 = await p2; and thus the p2 rejection is never handled.
Unhandled rejections are a Bad Thing™ (so much so that soon, Node.js will abort the process on truly unhandled rejections, just like unhandled exceptions — because that's what they are), so best to avoid the "get the promise then await it" pattern in your question.
Here's an example of the difference in timing in the failure case (using 500ms and 100ms rather than 5 seconds and 1 second), and possibly also the arguably-spurious unhandled rejection error (open the real browser console to see it):
const getValue1Async = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, "value1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error");
});
};
// This waits the full 500ms before failing, because it waits
// on p1, then on p2
(async () => {
try {
console.time("separate");
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
} catch (e) {
console.error(e);
}
console.timeEnd("separate");
})();
// This fails after just 100ms, because it doesn't wait for p1
// to finish first, it rejects as soon as p2 rejects
setTimeout(async () => {
try {
console.time("Promise.all");
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
} catch (e) {
console.timeEnd("Promise.all", e);
}
}, 1000);
Open the real browser console to see the unhandled rejection error.
And here we reject both p1 and p2, resulting in a non-spurious unhandled rejection error on p2:
const getValue1Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 500, "error1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error2");
});
};
// This waits the full 500ms before failing, because it waits
// on p1, then on p2
(async () => {
try {
console.time("separate");
const p1 = getValue1Async();
const p2 = getValue2Async();
const value1 = await p1;
const value2 = await p2;
} catch (e) {
console.error(e);
}
console.timeEnd("separate");
})();
// This fails after just 100ms, because it doesn't wait for p1
// to finish first, it rejects as soon as p2 rejects
setTimeout(async () => {
try {
console.time("Promise.all");
const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);
} catch (e) {
console.timeEnd("Promise.all", e);
}
}, 1000);
Open the real browser console to see the unhandled rejection error.
In a comment you've asked:
Side question: will the following force waiting for both (and discarding the results) await p1 && await p2?
This has the same issues around promise rejection as your original code: It will wait until p1 resolves even if p2 rejects earlier; it may generate an arguably-spurious (update: or temporary) unhandled rejection error if p2 rejects before p1 resolves; and it generates a genuine unhandled rejection error if both p1 and p2 reject (because p2's rejection is never handled).
Here's the case where p1 resolves and p2 rejects:
const getValue1Async = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, false);
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error");
});
};
(async () => {
try {
const p1 = getValue1Async();
const p2 = getValue2Async();
console.log("waiting");
await p1 && await p2;
} catch (e) {
console.error(e);
}
console.log("done waiting");
})();
Look in the real console (for the unhandled rejection error).
...and where both reject:
const getValue1Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 500, "error1");
});
};
const getValue2Async = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, "error2");
});
};
(async () => {
try {
const p1 = getValue1Async();
const p2 = getValue2Async();
console.log("waiting");
await p1 && await p2;
} catch (e) {
console.error(e);
}
console.log("done waiting");
})();
Look in the real console (for the unhandled rejection error).
¹ "...this code is clearly in an async function..." That was true in 2017 when this question and answer were written. Since then, top-level await happened/is happening.
I think this should work:
const [value1, value2] = await Promise.all([getValue1Async(),getValue2Async()]);
A more verbose example is below in case it helps in understanding:
const promise1 = async() => {
return 3;
}
const promise2 = async() => {
return 42;
}
const promise3 = async() => {
return 500;
// emulate an error
// throw "something went wrong...";
}
const f1 = async() => {
try {
// returns an array of values
const results = await Promise.all([promise1(), promise2(), promise3()]);
console.log(results);
console.log(results[0]);
console.log(results[1]);
console.log(results[2]);
// assigns values to individual variables through 'array destructuring'
const [value1, value2, value3] = await Promise.all([promise1(), promise2(), promise3()]);
console.log(value1);
console.log(value2);
console.log(value3);
} catch (err) {
console.log("there was an error: " + err);
}
}
f1();
Use .catch() and Promise.all()
Make sure you handle rejections correctly and you can safely use Promises.all() without facing unhandled rejections. (Edit: clarification per discussion: not the Error unhandled rejection but simply rejections that are not being handled by the code. Promise.all() will throw the first promise rejection and will ignore the rest).
In the example below an array of [[error, results], ...] is returned to allow ease of processing results and/or errors.
let myTimeout = (ms, is_ok) =>
new Promise((resolve, reject) =>
setTimeout(_=> is_ok ?
resolve(`ok in ${ms}`) :
reject(`error in ${ms}`),
ms));
let handleRejection = promise => promise
.then((...r) => [null, ...r])
.catch(e => [e]);
(async _=> {
let res = await Promise.all([
myTimeout(100, true),
myTimeout(200, false),
myTimeout(300, true),
myTimeout(400, false)
].map(handleRejection));
console.log(res);
})();
You may throw from within a catch() to stop waiting for all (and discard the results of the rest), however - you may only do it once per try/catch blocks so a flag has_thorwn need to be maintained and checked to make sure no unhandled errors happens.
let myTimeout = (ms, is_ok) =>
new Promise((resolve, reject) =>
setTimeout(_=> is_ok ?
resolve(`ok in ${ms}`) :
reject(`error in ${ms}`),
ms));
let has_thrown = false;
let handleRejection = promise => promise
.then((...r) => [null, ...r])
.catch(e => {
if (has_thrown) {
console.log('not throwing', e);
} else {
has_thrown = 1;
throw e;
}
});
(async _=> {
try {
let res = await Promise.all([
myTimeout(100, true),
myTimeout(200, false),
myTimeout(300, true),
myTimeout(400, false)
].map(handleRejection));
console.log(res);
} catch(e) {
console.log(e);
}
console.log('we are done');
})();
Resolves instead of Promises
const wait = (ms, data) => new Promise( resolve => setTimeout(resolve, ms, data) )
const reject = (ms, data) => new Promise( (r, reject) => setTimeout(reject, ms, data) )
const e = e => 'err:' + e
const l = l => (console.log(l), l)
;(async function parallel() {
let task1 = reject(500, 'parallelTask1').catch(e).then(l)
let task2 = wait(2500, 'parallelTask2').catch(e).then(l)
let task3 = reject(1500, 'parallelTask3').catch(e).then(l)
console.log('WAITING')
;[task1, task2, task3] = [await task1, await task2, await task3]
console.log('FINISHED', task1, task2, task3)
})()
As was pointed out in other answers, a rejected promise might raise an unhandled exception.
This one .catch(e => e) is a neat little trick that catches the error and passes it down the chain, allowing the promise to resolve, instead of rejecting.
If you find this ES6 code ugly see friendlier here.

Categories