ref – https://www.toptal.com/javascript/asynchronous-javascript-async-await-tutorial
Promises paved the way to one of the coolest improvements in JavaScript. ECMAScript 2017 brought in syntactic sugar on top of Promises in JavaScript in the form of async and await statements.
Async
Declaring a function as async will ensure that it always returns a Promise so you don’t have to worry about that anymore.
1 2 3 |
async function f() { return 1; } |
The word “async” before a function means one simple thing: a function always returns a promise.
1st example
1 2 3 4 5 |
async function f() { return 1; } f().then(success => console.log(success)); // 1 |
We can explicitly return a the static function for class Promise:
1 2 3 4 5 |
async function f() { return Promise.resolve(1); } f().then(success => console.log(success)); // 1 |
So, async ensures that the function returns a promise, wraps non-promises in it.
Await
The await operator is used to wait for a Promise.
It can only be used inside an async function.
The await expression causes async function execution to pause until a Promise is fulfilled, that is resolved or rejected, and to resume execution of the async function after fulfillment. When resumed, the value of the await expression is that of the fulfilled Promise.
The syntax:
1 2 |
// works only inside async functions let value = await promise; |
The keyword await makes JavaScript wait until that promise settles and returns its result.
If there was no await, JS’s single thread will execute the async function, and down the main execution steps turn by turn. So that then end of your program will be executed even when your function is not done.
It won’t work because there is no await…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
function doubleAfter2Seconds(x) { return new Promise( function( resolve, reject ) { setTimeout(() => { console.log(`doubleAfter2Seconds: ${x*2}`) resolve(x * 2); }, 2000); }); } async function addAsync() { console.log("running addAsync") // this won't wait. execution runs straight through const a = doubleAfter2Seconds(2); const b = doubleAfter2Seconds(3); return a+b; } addAsync().then(function(result) { console.log("here-----"); console.log(result); }); |
doubleAfter2Seconds returns Promise object. But since we don’t wait for it to resolve, the returned value is the Promise object. And not th resolved value that we want.
output:
running addAsync
trash.html:79 a: [object Promise]
trash.html:81 b: [object Promise]
trash.html:88 —–here—–
trash.html:89 [object Promise][object Promise]
trash.html:67 doubleAfter2Seconds: 4
trash.html:67 doubleAfter2Seconds: 6
As you can see, when your function is finally done, the result will be stranded. With nothing to execute the returned result.
Using await, it makes the JS thread wait until the promise is returned from the async function. Then proceeds to process it.
1 2 3 4 5 6 7 8 |
async function addAsync() { console.log("running addAsync") const a = await doubleAfter2Seconds(2); const b = await doubleAfter2Seconds(3); return a+b; } |
output:
running addAsync
trash.html:67 doubleAfter2Seconds: 4
trash.html:79 a: 4
trash.html:67 doubleAfter2Seconds: 6
trash.html:81 b: 6
trash.html:88 —–here—–
trash.html:89 10
In a more simple example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
async function f() { // creates new Promise let promise = new Promise((resolve, reject) => { // resolves after 1 sec setTimeout(() => resolve("done!"), 1000) }); // The "await" tells execution to wait here, and resumes when the promise settles let result = await promise; // wait till the promise resolves (*) console.log(result); // "done!" } f(); |
await literally makes JavaScript wait until the promise settles, and then go on with the result.
That doesn’t cost any CPU resources, because the engine can do other jobs meanwhile: execute other scripts, handle events etc.
(JS only has 1 thread, which can skip around and execute different tasks, doing a little at a time)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// It's pretty clear that the Async/Await version of the code is much shorter and easier to read. // Other than the syntax used, both functions are completely identical // - they both return Promises // The async keyword will automatically create a new Promise and return it. async function getJSONAsync (){ return 3; // returns valid or invalid } console.log("--- begin ---"); let p = getJSONAsync(); console.log(p); p.then( function(result) { console.log(result); }); console.log("--- end ---"); |
output:
— begin —
Promise {
Promise[[PromiseStatus]]: “resolved”
[[PromiseValue]]: 3
3
If you wish to await two or more promises in parallel, you must still use Promise.all.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
var max = 12; var max2 = 5; var max3 = 8; var promise1 = new Promise(function(resolve, reject){ let interval = setInterval(function(){ console.log("promise1: "+max--); if (max === 1) { resolve(1); clearInterval(interval); } }, 1000); }); var promise2 = new Promise(function(resolve, reject){ let interval2 = setInterval(function(){ console.log("promise2: "+max2--); if (max2 === 2) { resolve(2); clearInterval(interval2); } }, 1200); }); var promise3 = new Promise(function(resolve, reject) { let interval3 = setInterval(function(){ console.log("promise3: "+max3--); if (max3 === 3) { resolve(3); clearInterval(interval3); } }, 1300); }); Promise.all([promise1, promise2, promise3]).then(function(values) { console.log(values); }); // expected output: Array [3, 42, "foo"] |
Examples of using both
Asynchronous execution
Await is used inside of the async.
When we call a function with async, it
1) returns a Promise object
2) runs asynchronously
You can see this asynchronous activity going on because as the function f runs, the JS execution also continues
down and logs “grandma says:….come to dinner…!!….”.
using await and then at the same time
Whenever we deal with async functions, we must return a Promise. Thus we create a Promise, and make it run a setTimeout
function which is asynchronous. The setTimeout executes code after 3 seconds.
However, in our code, the Promise does not get returned right away due to the “await” statement. Rather, we must wait for the Promise to finish before moving on.
Therefore, the log statements and return Promise MUST wait for the Promise to resolve FIRST.
After the Promise resolves, we return the promise object.
Finally, because the promise object was already resolved, the
then() gets executed right away.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
async function f() { console.log("start f()"); // 1 // creates new Promise let promise = new Promise((resolve, reject) => { // 2) create new Promise object setTimeout(function() { resolve("oh yea!"); // 5) resolves! }, 3000) // 4) waits 3 seconds }); await promise; // 3) waits for the promise object to resolve. // 6) runs the rest of the statements console.log("oh no...! runaway return result....!!"); return promise; } f().then(success => { // 7) Runs the then block because the returned Promise // has already resolved console.log("1ST THEN:" + success); }); |
No Await
If we remove the await, the promise gets created, then since there is no “await”, we continue
with the log statements, and then returns the promise object.
Then the Promise executes the setTimeout, and resolves after 3 seconds.
What’s waiting for the resolve will be the then().
The execution will run in this order:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
async function f() { console.log("start of f() "); // creates new Promise let promise = new Promise((resolve, reject) => { // 2) create new Promise object console.log("create new Promise object"); setTimeout(function() { console.log("waited 3 sec"); // 5) resolve("oh yea!"); // 6) resolves! }, 3000) // 4) waits 3 seconds }); // 7) runs the rest of the statements console.log("return the Promise object"); return promise; } f().then(success => { // 8) Runs the then block console.log("1ST THEN:" + success); }); |
start of f()
create new Promise object
Wait for the Promise to resolve
here….
there….
everywhere…!!….
return the Promise object
waited 3 sec
1ST THEN:oh yea!
Another Example
Another example shows that await actually stops execution on the await line within that async function. While it waits, the JS execution will still move on
to execute after the async functionK
This is shown at 10, where the new Promise object has been created and will be returned. When it returns the Promise object, execution will need wait for the Promise to finish. Since the Promise takes 2 seconds, JS execution will continue after the async func onto line 11.
After the 2 seconds, we resolve the Promise at line 12. Then go on to 13, which we now have the passed in parameter value from the Promise’s resolve.
Whenever you “await” for a Promise, the rest of the async function will not run. Because it is “awaiting” your Promise to finish.
However, it will continue to execute the code that takes place AFTER the async function.
Take note at var x, it is awaiting a Promise to resolve inside of resolveAfter2Seconds…while it is waiting, execution continues at 11.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
function resolveAfter2Seconds(x) { console.log("resolveAfter2Seconds"); // 5) console.log("let's return a new Promise"); // 6) return new Promise(resolve => { // 7) console.log("creating new Promise..."); // 8) setTimeout(() => { // 9 resolve(x); // 12 }, 2000); console.log("Finish creating new Promise"); // 10 }); } async function f1() { console.log("start of f1()"); // 3) // await Promise before moving on var x = await resolveAfter2Seconds(10); // 4) // 13) x is now valid // 14) console.log("Promise resolves"); console.log("resolve parameter is: "+x); } console.log("...start of PROGRAM..."); // 1) f1(); // 2) console.log("hei hei hei"); // 11) |
One more…!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
console.log("here..."); // 1) async function f() { console.log("start of f()"); //4 ) let promise = new Promise((resolve, reject) => { console.log("creating new Promise"); // 5) // 6 setTimeout( () => resolve("done!"), // 10 The promise has been resolved! 2000) console.log("finish creating Promise"); // 7) }); // 8) WE NOW NEED TO WAIT HERE BECAUSE OF THE "AWAIT". However // execution will continue further at 9) let result = await promise; // wait till the promise resolves (*) console.log("result is: " + result); // 11) Since the promise was resolved, we continue here. console.log("end of f()"); // 12 } console.log("there..."); // 2) f(); //3) // 9) because async f() is waiting...JS's execution continues further. console.log("Eveerry wheeereeee.....") |