๐JavaScript: Async/Awaitโ
Here you will get a complete explanation of Async/Await with image visualization.
Hey there! Welcome to my article, as you will scroll down you will going to get a great explanation with images..so must read๐.
Synchronous and Asynchronous code๐ฉโ๐ป
JavaScript is a synchronous single-threaded programming language. What this means is that it can perform only one operation at a time. When one operation is executed other operations are blocked and have to wait.
Now, what if the code is asynchronous?๐ค
It works in the opposite way. When asynchronous code
is executed it doesnโt block other code. Other code can still be executed while the asynchronous operation is being executed. That asynchronous code is basically running in the background making space for other operations to take place.
A quick ride ๐ดโโ๏ธ
In JavaScript, you can code async
tasks in 3 ways.
- Callback function ๐ค
The first approach is using callbacks
. When an async operation
had been completed, a callback function (meaning call me back when the operation has been completed) is executed:
const callbackFunction = result = {
// Called when the operation completes
};
asyncOperation(params, callbackFunction);
But as soon as you handle multiple async operations
, the callback functions nest into each other ending in callback hell
.
- Promises ๐ค
A promise
is a placeholder object for the results of an async task. With the use of promises, you can handle the async operations easier.
const promise = asyncOperation(params);
promise.then(result => {
// Called when the operation completes
});
Have you seen the .then().then()...then()
chains of promises ๐๐๐๐๐? An issue of promises is their verbosity.
- Async ๐/Awaitโฑ๏ธ
Finally, the third attempt is the async/await
syntax (starting ES2017). It lets you write async code in a concise and sync manner:
(async function() {
const result = await asyncOperation(params);
// Called when the operation completes
})();
In this post I'm going to explain, step by step, how to use async/await
in JavaScript.
Working of Async/Await ๐
We can explicitly create promises
using the Promise object
, whether it was by typing new Promise(() => {}), Promise.resolve, or Promise.reject
.
Instead of explicitly using the Promise object
, we can now create asynchronous functions that implicitly return an object! This means that we no longer have to write any Promise object ourselves.
Although the fact that async
functions implicitly return promises is pretty great, the real power of async functions
can be seen when using the await keyword!
With the await keyword, we can suspend the asynchronous function while we wait for the awaited value to return a resolved promise
. If we want to get the value of this resolved promise
, like we previously did with the then() callback
, we can assign variables to the awaited promise value!
So, we can suspend an async function? Okay great but.. what does that even mean?
Let's see what happens when we run the following block of code:
const one = () => Promise.resolve('One');
async function myFunc() {
console.log('In function!');
const res = await one();
console.log(res);
}
console.log('Before function!');
myFunc();
console.log('After function!');
Hmm.. What's happening here?๐ฏ
First
, the engine encounters a console.log
. It gets popped onto the call stack
, after which the Before function!
gets logged.
Then, we invoke the async function myFunc()
, after which the function body of myFunc()
runs. On the very first line within the function body, we call another console.log
, this time with the string In function!. The console.log
gets added to the call stack, logs the value, and gets popped off.
The first thing that happens is that the value that gets awaited gets executed: the function one in this case. It gets popped onto the call stack, and eventually returns a resolved promise. Once the promise has resolved and one returned a value, the engine encounters the
await keyword.
When encountering an await keyword, the async function gets suspended. โ๐ผ The execution of the function body gets paused, and the rest of the async function gets run in a microtask instead of a regular task!
Now that the async function myFunc is suspended as it encountered the await keyword, the engine jumps out of the async function and continues executing the code in the execution context in which the async function got called: the global execution context in this case! ๐๐ฝโโ๏ธ
Finally, there are no more tasks to run in the global execution context! The
event loop
checks to see if there are any microtasks queued up
: and there are! The async myFunc
function is queued up after resolving the value of one. myFunc
gets popped back onto the call stack
and continues running where it previously left off.
The variable res
finally gets its value, namely the value of the resolved promise that one returned! We invoke console.log
with the value ofres:
the string One
in this case. One
gets logged to the console and gets popped off the call stack! ๐
Finally, all done! Did you notice how async functions
are different compared to a promise then? The await keyword
suspends the async
function, whereas the Promise
body would've kept on being executed if we would've used then!
Hmm that was quite a lot of information! ๐คฏ No worries at all if you still feel a bit overwhelmed when working with Promises
, I personally feel that it just takes experience to notice patterns and feel confident when working with asynchronous JavaScript.
I am also trying too......๐คญ๐
However, I hope that the "unexpected" or "unpredictable" behavior that you might encounter when working with async JavaScript makes a bit more sense now!
Thanks for reading! ๐