Understanding JavaScript: Promises, Async & Await!!

Nitin Gupta
The Startup
Published in
6 min readSep 13, 2020

--

Analogy

We all know the importance of promises in our life. We even have a special day dedicated to it :) But how well do we know the importance of promises in JavaScript? Well if you don’t know it yet, it’s a great time to know it because they are becoming more and more popular. So what are promises? Let’s try to understand it through an analogy.

Suppose you are a top class rapper and you haven’t released an album for a while and fans are asking for it day and night. So what you do is that you “promise” them that whenever it will be out, all of them would be notified. To get this done you give your fans a list. They can fill in their email addresses, so that when the album becomes available, all the subscribers instantly receive it. And even if something goes wrong, say a pandemic, so that you can’t release the album, they will still be notified.

Now everyone is happy: You, because the people don’t crowd you anymore, and fans, because they won’t miss any news on the album.

This is a real-life analogy for things we often have in programming:

  1. A “producing code” that does something and may take time. That’s a “rapper”.
  2. A “consuming code” that wants the result of the “producing code” once it’s ready. Many functions may need that result. These are the “fans”.
  3. A promise is a special JavaScript object that links the “producing code” and the “consuming code” together. In terms of our analogy: this is the “subscription list”. The “producing code” takes whatever time it needs to produce the promised result, and the “promise” makes that result available to all of the subscribed code when it’s ready.

JavaScript promises are much more complex than a simple subscription list: they have additional features and limitations. But it’s fine to begin with.

What are Promises?

A Promise is an object representing the eventual completion or failure of an asynchronous operation. Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function. Don’t know about callbacks? Don’t worry get to know callbacks here.

Promises are eager, meaning that a promise will start doing whatever task you give it as soon as the promise constructor is invoked.

The function passed in the Promise constructor is called the “executor” and the arguments in the function “resolve” and “reject” are callbacks provided by JavaScript itself. Our code is inside the executor only and thus tends to bind in a Promise. We all have been using Promises whether you know about them or not!!

Working of Promises

A promise is an object which can be returned synchronously from an asynchronous function. It will be in one of the 4 possible states:

fulfilled — The action relating to the promise succeeded

rejected — The action relating to the promise failed

pending — Hasn’t fulfilled or rejected yet

settled — Has fulfilled or rejected

There can be only a single value, either resolve or reject.

  • state — initially “pending”, then changes to either “fulfilled” when resolve is called or “rejected” when reject is called.
  • result — initially undefined, then changes to value when resolve(value) called or error when reject(error) is called.

So the executor eventually moves promise to one of these states:

Now let us see an example code to get a better understanding:

In this code we can see that:
The executor is called automatically and immediately (by new Promise).

  1. The executor receives two arguments: resolve and reject. These functions are predefined by the JavaScript engine, so we don’t need to create them. We should only call one of them when ready.
  2. After three second of “processing” the executor calls resolve(“done”) to produce the result. This changes the state of the promise object:

Take another example:

The call to reject(…) moves the promise object to “rejected” state:

When we reject() with a value, we should always pass an Error object. Generally we want two possible resolution states: the normal happy path, or an exception — anything that stops the normal happy path from happening. Passing an Error object makes that explicit.

Consumers: then, catch, finally

A Promise object serves as a link between the executor (the “producing code” or “rapper”) and the consuming functions (the “fans”), which will receive the result or error. Consuming functions can be registered (subscribed) using methods .then(), .catch() and .finally().

Here’s how you can use the above created Promise:

Then

then() takes an argument, a callback for a success case and executes when the promise is resolved.
If we are interested we can also give .then( ) a second argument, a callback for a failure too which will execute when the promise is rejected.

Catch

If we’re interested only in errors, then we can use null as the first argument .then(null, errorHandlingFunction). Or we can use .catch(errorHandlingFunction), which is exactly the same.

Finally

Just like there’s a finally clause in a regular try {…} catch {…}, there’s finally in promises.

The call .finally(function) is similar to .then(function, function) in the sense that function always runs when the promise is settled: be it resolve or reject.

finally is a good handler for performing cleanup, e.g. stopping our loading indicators, as they are not needed anymore, no matter what the outcome is.

Promise.all()

Consider a situation where you want to have to make multiple promises (say more than 50 or 100). It would be not at all convenient to call each one of them separately. So for that we have Promise.all()

Promise.all() takes an array of promises and returns their values when all promises are resolved, or throws an error. Let us see how it works.

In the above example, all the promises are executed and then the results are returned. But if any promise throws an error then it stops the next promises which are to be executed. Here you will see that I have used some words like “async” and “await”. If you don’t know about them then don’t worry I will explain these in the next section.

Important Promise Rules

Promises following the spec must follow a specific set of rules:

  • A promise or “thenable” is an object that supplies a standard-compliant .then() method. Thus every promise must supply a .then() method
  • A pending promise may transition into a fulfilled or rejected state.
  • A fulfilled or rejected promise is settled, and must not transition into any other state.
  • Once a promise is settled, it must have a value (which may be undefined). That value must not change.

Async and Await

Async is a function that returns a promise. Async/Await makes code look synchronous although it is asynchronous code.

Let us see it:

async and await are syntactic sugar on top of Promises. async returns promise and await waits until the promise is completed.

Everyone can just simply understand by seeing the code. That’s the beauty of async and await. Less code more readable just looks simple. But remember there can be no await without async function.

Final Words

So that’s all you need to know about Promises, async and await to get started with them. Hope you will be more confident in these after reading this article and I was able to fulfill your purpose of coming here and you are not thinking that it was a waste of your time. See you next time!

--

--

Nitin Gupta
The Startup

Elevating digital realms with a full-stack touch, merging design finesse with product mastery. I write about JS and my journey as a developer