Make your code cleaner, more readable with the best feature in ES8
In this piece, we are going to learn about the two new functions which came with the release of ES8 i.e
With the help of
async-await, we can now write perfectly synchronous-looking code while letting them handle the asynchronous tasks in the back.
What is an event loop and how does it work?
What is an event loop and how does it work?
To understand the concept behind an event loop, we first need to look at how a call stack works. A call stack is like a log of the current execution of the program.
The call stack is very similar to a normal stack with the same operations, namely push and pop. A stack data structure follows the concept of LIFO(Last in First Out), and by push, we mean putting something into the stack.
Similarly, pop means to remove something from the stack. When the function at the very top of the stack finishes execution, it gets removed from the call stack as it follows the LIFO property.
How call stacks work
Now that we know what properties and concepts a call stack follows, let’s look at how it works practically. Have a look at this code snippet:
In this code, we have three
console.log() statements inside the
main() function, which simply logs the string value in the terminal.
Two of these statements are presented right at the top and bottom, at lines two and six respectively. The other statement is between the two, wrapping itself inside a
setTimeout call (using a browser API, which we’ll talk about later) with a zero millisecond wait time.
How the event loop works
- As the program starts, it enters into the main function. As a result,
main()pushes itself into the call stack first as a frame. After entering the main function, the first statement that executes is
console.log(“First”). This statement also pushes itself into the stack by the browser. Upon execution, it pops the frame out and displays
“First”in the console.
- Upon execution of the first statement, the next statement pushes itself to the stack(
setTimeout). This statement uses a browser API to delay the execution time of the callback function present inside. This frame pops itself out of the stack after handing over the
middle()function to the browser for execution. This is because it is a browser API, and it executes at a certain time of zero milliseconds.
- After this executes, the third statement, or
console.log(“Third”), pushes itself to the call stack for execution. This happens while the timer runs in the background to execute the
middle()function. Once the browser pushes this frame into the call stack, it executes and displays
“Third”in the console, popping out the final frame.
setTimeoutfunction gets a zero millisecond delay, causing the callback to add to the event queue as soon as the browser receives it, since the execution time has already expired.
- As soon as the last statement executes and pops out of the call stack, the
main()frame pops out too, thus making it empty. For the browser to push any message from the event queue to the call stack, it has to be completely empty. This is why, even though the delay was zero milliseconds, the callback still has to wait.
- Finally, the statement
console.log(“Second”)inside the callback
middle()pushes itself into the call stack and executes, thus printing
“Second”into the console.
What Are Promises?
How promises work
Imagine that you have promised a friend to buy him his favorite book. To fulfill this promise, you need to:
- Go into any book store where you’d possibly find the book.
- Buy it and bring it back to him
While this is happening, all that your friend is doing is waiting for you.
Imagine if the promise was something else, like getting into your favorite company or buying a very expensive car. These promises will take a much longer time to fulfill, thus a greater waiting time.
Promises contain three states:
How promises work
A promise is an object which is able to return itself synchronously from an asynchronous function.
If you are requesting something from a program(promise), it stays in the pending state until the time it is over/complete.
Once the conditions are fulfilled, then the promise is resolved,
resolve(), or rejected,
reject(). Once a promise settles, it cannot resettle.
Take a look at this code snippet:
In this code, we pass on a promise which takes two parameters,
reject. It can have any name, but this examples helps us to better understand.
In this example, let’s say the promise is resolved — the work it is supposed to do is complete — and we are passing it on using the
Since the function is already resolved, it won’t throw any error. Instead, it will execute the success function, printing the message in the
first .then() method.
Take a look at this code snippet:
In this code snippet, we do the same thing, but let’s say the request/result we wanted to get from this promise was rejected/failed.
In that case, we are passing on a
reject() method, which the
.then() method handles again. It automatically invokes the error function(since it comes under
reject() and is an error), which in turn displays the error message.
Important rules of promise
A promise’s standard has already been defined by the Promises/A+ specification community. Typically, promises always follows a specific set of rules:
- A promise object should always supply a
promisewhich is pending will always transition back to either resolve or reject.
- A promise, whether resolved or rejected, is always settled and must not transition into any other state.
- Once a promise settles, it has some value. That value must not change.
What Is Async-Await?
Until now, we have been using promises to deal with asynchrony in our code. That works fine, but the new release of
async-await in ES8 has been even more helpful in allowing us to write super smooth, synchronous-looking code.
According to MDN(Mozilla Developer Network):
“An asynchronous function is a function which operates asynchronously via the event loop, using an implicit promise to return its result. But the syntax and structure of your code using async functions is much more like using standard synchronous functions.”
Now, let’s unpack the meaning of the above sentence.
- It is implicitly related to promises and uses it to return the result.
- The syntax structure is the same as writing a synchronous function.
Async-await is just an extension of promises. It enables us to write promise-based code synchronously without blocking the execution thread. An
async function implies that a promise value will return.
It works asynchronously via the event loop. An
To understand this, let’s look at this code:
Running the above code will give us an alert output with the string message
This means a resolved promise successfully returns by default. If it was the other way around, the
.then() method wouldn’t execute.
await keyword handles the waiting part of the
async-await block. This operator is used to wait for a promise.
It is functional inside the
async block and waits for the promise to return a result.
Await only makes the
async function block wait, not the whole program.
Let’s understand this by looking at this code:
When we call the
display() function at the end, it invokes the
AwaitExample() function enclosed in an
What this means is that until the time the promise resolves itself on the
AwaitExample() function, the
await keyword will hold the execution of the
It won’t stop the program from running, but will only hold the
display() method. After two seconds, or 2000 milliseconds, the promise resolves, which the console then displays.
Rules to follow while using async-await
We should take care of a few rules before we use async-await in our code block. These rules are necessary to make your function work properly.
Let’s take a quick look:
- We can’t use the
awaitkeyword in a normal function. You must have an
asyncfunction to be able to use
awaitinside of it.
Async-awaitmakes execution always sequential. Having a parallel execution is must faster.
Async-await is very powerful, but it comes with caveats. If we use them properly, they help to make our code very readable and efficient.
Sequential vs. Parallel Execution
As the name suggests, sequential execution means executing your code in a sequence. This process is a little slower if we compare it to parallel execution, since in sequential execution one line has to finish processing before it can execute the next line.
Let’s understand this with the help of an example. Take a look at this code snippet:
In this example, we are just returning two promise functions. But when the first one calls itself, it takes two seconds to execute. Meanwhile, the other
await function is just sitting there.
After the first promise resolves, only then does the next
await start execution. This process is time-consuming when we compare it to parallel execution.
A much more efficient way of handling faster execution is through parallel execution.
Basically, it won’t stop the execution of other
await methods if there is more than one present.
It parallel processes all the
await methods. This type of processing uses the
promise.all() method. Let’s understand this with the help of this example:
In this code snippet, we use a method called
promise.all(). The function of this method is to resolve all the promises inside of the iterable first, and then returns the final result.
Thus, since the individual functions run in parallel, execution is much faster when we compare it to sequential execution.
Error Handling in Async-Await
Another very nice feature about asynchronous function is that
async-await can handle errors synchronously, as well as with the help of try and catch block.
Depending upon whether the value gets resolved or rejected, our try and catch block will display the required output, and the process flow will be synchronous as well.
Let’s take a look at an example:
In this example, we observe that the Promise gets either
rejected depending on the random value generated by the
That brings us to the end of this piece. I hope you enjoyed learning about what impact
Thank you for reading. Peace out. ✌️