JavaScript's asynchronicity - Promises, callbacks and async/await
Tags: javascript, beginners, tutorial, webdev
Published: 2021-08-25 13:09:23

One of the core concepts of JavaScript is asynchronicity, which means doing many things simultaneously. It's a solution for avoiding your code being blocked by a time-intensive operation (like an HTTP request). In this article, you're going to learn the basic concept of asynchronicity and how to use it in JavaScript.

-----

## But before we start...
... we need to cover some computer theory. Programming is the process of telling your computer what it's supposed to do, we communicate with it using code. Every code is just a set of instructions for our machine that we want to execute. Every line of our **code** is executed by a so-called **thread**. A **thread** is executing only one instruction at a time. Let's analyze this pseudo-code:

```
set x to 10
set y to 5
add x to y save result to r
display r
```

When we execute this code, a **thread** is going to firstly set our variables `x` value to 10, **THEN** it will set `y` to 5, **AFTER THAT** it is going to add these two numbers together and save the result to our variable `r` and at the end it will display the value of r. The keywords are **THEN** and **AFTER THAT**, our thread can't simultaneously set `x` to 10 and `y` to 5, it has to wait with setting `y` until setting `x` is done.  This is type of code is named synchronous code - every instruction is executed one after another. With such simple operations, we're not going to find any issues, but what when we want to execute something that is time-consuming? Like downloading an image? Well, there's the tricky part. 

Such an operation is a **blocking code** because it stops our thread from performing anything else until the image is downloaded. We don't want our users to wait every time such instruction occurs. Imagine downloading a meme and when it's happening your computer can't do anything else - your music player stops, desktop freezes, etc. - using such computers would be a pain. As you probably noticed, such things are not happening, you can listen to music, watch a video on YouTube and code your breakthrough project all at the same time. That's because computer engineers found a solution to this problem.

Wise people once thought, if one thread can execute one operation at a time, couldn't 16 threads execute 16 operations in parallel? Yes, they can - and that's the reason why modern CPUs have many cores and every core has many threads. Programs using many threads are **multi-threaded**.

![A diagram showing how multi-threaded apps work.](https://blog.danieo.me/content/images/2021/08/Multi-Threaded-1-.jpg)

The problem with JavaScript is that it's not **multi-threaded**, JavaScript is **single-threaded**, so it can't use many threads to do many operations at the same time. We're left with the same problem again - is there any other way to fix this? Yes! It's writing **asynchronous code**.

Let's assume that you want to fetch posts from your server every time your user scrolls your website. For this, we need to make an API call. API calls are just HTTP requests, which means that our browser making such call needs to establish a connection to our server, then our server processes the request, then sends it back, then our browser needs to process it... it's all time-consuming, and waiting for it to finish will block other interactions on our website, but it would only happen if our code was synchronous. Most time-consuming things like HTTP requests are mostly handled not by our **main thread**, but by lower-level APIs implemented in our browser. **Asynchronous code** uses this principle. We don't have to wait for our browser to finish the HTTP request, we can just inform the browser that we need to make an HTTP request, the browser will handle it and report to us with the result - in the meantime, other code can be executed on the **main thread**.


![A diagram showing how asynchronous code works](https://blog.danieo.me/content/images/2021/08/Asynchronous.jpg)

You probably noticed that **asynchronous** code is similar to **multi-thread** code. Well, kind of. Both help us to solve the problem with **blocking code**, but **asynchronous code** in JavaScript is **pseudo-parallel**. For example, if we want to run two compute-intensive calculations in parallel we can't do it until the execution is handled by something else (like a lower-level API of our browser). For real parallelism in JavaScript, we can use **WebWorkers**, which run specified code in the background. However, **WebWorkers** are not today's topic, so I won't talk about them - for now. 😉

Ok, that's enough theory. How we can write this asynchronous code in JavaScript? There are two major ways to do it, the older method using **callbacks** and the newer method using **Promises**. It's time to look at them in deep.

## Callbacks
Earlier I said that when our asynchronous operation is done, we inform our **main thread** about it. The older way to report back is using a **callback**. A **callback** is basically a function that is called when our task is done. It can also carry arguments with data like a result of the asynchronous task. Let's analyze some examples.

We are going to fetch information about Charmander from [pokeapi.co](https://pokeapi.co/) using `XMLHttpRequest` API.

```javascript
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://pokeapi.co/api/v2/pokemon/charmander', true);
xhr.responseType = 'json';
xhr.onload = (e) => {
  if (xhr.status === 200) {
    console.dir(xhr.response);
  } else {
    console.error('Something went wrong...');
  }
};
xhr.send(null);
```

The first 3 lines are just configuring the `XMLHttpRequest` object. The thing that interests us the most is `xml.onload`, because here we specify our callback using an arrow function. When we send our request, the browser is going to handle it and when it's done it's going to call our callback function in which we can further process the received data.

Another common example of using callbacks to handle asynchronous tasks are **Event Listeners**. Look at the code below.

```javascript
const button = document.getElementById('myButton');
button.addEventListener('click', (event) => {
  console.info('Button clicked!');
});
```

We get our button element using its ID, then we attach a **listener** to its `click` event. Listener functions are nothing else than just **callbacks**. Our arrow function is called every time the user clicks this button. This whole process is not blocking code, because we don't have to wait for the click in our main thread. Events are handled by the browser and we only attach a callback that **is called when the click is done**.

One more example. **Timeout** and **Intervals** are also asynchronous.

```javascript
const timeout = setTimeout(() => {
  console.info('Boo!');
}, 5000);
```

The **Timeout** or **Interval** handler function is also a callback and it's called only after a certain time has been deducted. The whole time measurement code is handled by our browser's components, not by us, so we are only informed when the right amount of time has passed.

Now let's combine some of these examples as a recap.

```javascript
const button = document.getElementById('myButton');
button.addEventListener('click', (event) => {
  console.info('Button clicked!');
});

const request = setTimeout(() => { // This timeout is going to simulate a very long HTTP request
  console.info('Response received!');
}, 5000);
```

In this code, we attach a listener to our button and make an HTTP request. If you run this example, you can see that you can click the button despite the fact that the HTTP request is being made. You don't have to wait with the request until the button is clicked, nor do you have to wait with handling the button click until the HTTP request is done - no operation is blocked. That's the power of asynchronicity!

## Promises 

The modern way to handle asynchronicity in JavaScript is to use **Promises**. You can think of them like a promise made by people. It's not the result of something, it's just a promise that something will be done in the future (or not). If your roommate promises you to take out the trash this week, she's telling you that she will do it in the future, but not now. You can focus on your things and after some hours your roommate is going to tell you that the trashcan is empty and that she fulfilled her promise. Your roommate can also tell you, that she couldn't fulfill it because there is a raccoon living in your trashcan and it behaves aggressively when you try to take out the litter bag. In this case, she couldn't keep this promise, because she doesn't want to be attacked by an aggressive raccoon.

![A photo of a raccoon](https://blog.danieo.me/content/images/2021/08/vincent-dorig-Y60q9L_ZS00-unsplash.jpg)
Remember, not every raccoon is aggressive! Photo by [Vincent Dörig](https://unsplash.com/@vincentdoerig?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on Unsplash

A **Promise** can be in one of three states:
* _pending_ - This is an initial state, the Promise is running and we don't know if it's fulfilled or something went wrong.
* _fulfilled_ (or resolved) - Everything is ok. The Promise has completed its task successfully.
* _rejected_ - Something went wrong and the operation failed. 

So let's create our first **promise**.

```javascript
const promise = new Promise((resolve) => {
  setTimeout(resolve, 3000);
});
```

We are creating a new **Promise** object by calling the **Promise** constructor. As you can see in this example the constructor of a **Promise** object takes an arrow function as an argument. This argument is called an **executor** or **executor function**. The **executor** is going to be called when we are creating our **Promise** object and it is the connector between your **Promise** and the result. The executor takes two arguments a **resolve function** and a **reject function** - both of them are used to control your Promise. Resolve is used to mark our promise as fulfilled and return result data. Reject is used to notify that something is wrong and the Promise is not going to be fulfilled - it's _rejected_. Reject like resolve can also carry data, in most cases, it carries information about why the **Promise** was not fulfilled.

Resolving and rejecting promises can be handled by methods, provided by the **Promise** object. Take a look at this code.

```javascript
const promise = new Promise((resolve) => {
  setTimeout(resolve, 3000);
});

promise.then(() => {
  console.info('3 seconds have passed!');
});
```

Our promise is very simple, our executor is going to create a Timeout and call our resolve function after 3 seconds. We can intercept this information using `.then()` by providing a **callback** to it. `.then()` takes two arguments, the first is a callback called, when the Promise is fulfilled, the second one (not seen in this example) is a callback called when the Promise is rejected. But for handling rejected promises we can use a more convenient method - `.catch()`. Let's modify our example.

```javascript
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const number = Math.floor(Math.random()*100);

    if (number % 2 === 0) {
      resolve(number);
    }

    reject(new Error('Generated number is not even!'));
  }, 3000);
});

promise.then((result) => {
  console.info('Promise fulfilled!');
  console.info(`${result} is even.`);
}).catch((error) => {
  console.info('Promise rejected!');
  console.error(error);
});
```

This code after 3 seconds is going to generate a random number and check if it's even or not. If it's even then the Promise is resolved and we return the even number, if not, we reject the Promise with an error message. `.catch()` as an argument accepts a callback that is called when the Promise is rejected. 

We can also reject Promises by throwing an error.

```javascript
const promise = new Promise((resolve) => {
  throw new Error('Error message');
});

promise.then((result) => {
  console.info('Promise fulfilled!');
}).catch((error) => {
  console.info('Promise rejected!');
  console.error(error);
});
```

However, this has some limitations. If we throw an error inside an asynchronous function like Timeout's callback in our example, `.catch()` will not be called and the thrown error will behave as an _Uncaught Error_.

```javascript
const promise = new Promise((resolve) => {
  setTimeout(() => {
    const number = Math.floor(Math.random()*100);

    if (number % 2 === 0) {
      resolve(number);
    }

    throw new Error('Generated number is not even!'); // This is an Uncaught Error
  }, 3000);
});

promise.then((result) => {
  console.info('Promise fulfilled!');
  console.info(`${result} is even.`);
}).catch((error) => {
  console.info('Promise rejected!');
  console.error(error);
});
```

Also, you need to remember that every error thrown after calling `resolve()` is going to be silenced.

```javascript
const promise = new Promise((resolve) => {
  resolve();
  throw new Error('Error message'); // This is silenced
});
```

Beside `.then()` and `.catch()` we also have a third method - `.finally()`. Finally is called when the Promise is done, it doesn't bother if it was resolved or rejected, it runs after `.then()` and `.catch()`.

```javascript
const promise = new Promise((resolve, reject) => {
  if (Math.random() < 0.5) {
    resolve('Promise fulfilled');
  }

  reject(new Error('Promise rejected'));
});

promise.then((result) => {
  console.dir(result); // Runs only when the Promise is resolved
}).catch((error) => {
  console.error(error); // Run only when the Promise is rejected
}).finally(() => {
  console.dir('Promise has finished its work'); // Run everytime the Promise is finished
});
```

Now, let's analyze a real-case example.

```javascript
const fetchCharmanderData = fetch('https://pokeapi.co/api/v2/pokemon/charmander');

fetchCharmanderData.then((response) => {
  if (response.status === 200) {
    return response.json();
  } else {
    throw new Error(response.statusText);
  }
}).then((data) => {
  console.dir(data);
}).catch((error) => {
  console.error(error);
});
```

This code will fetch information about Charmander from [pokeapi.co](https://pokeapi.co/) but it uses the new, promise-based **fetch API**. Fetch will make an HTTP request and return a Promise for it. When the data is fetched, we process the response. If we received a HTTP status 200 (OK) we are returning the JSON representation of the response body, if the status code is different (like 404 not found or 500 internal server error) we throw an error with a status message. As you see, we are using `.then()` twice. The first time is used, as I mentioned, to process the response, the second time we use `.then()` to process a second Promise. `response.json()` also returns a Promise (JSON parsing can also take some time so it can also be blocking code, that's why we want to make it asynchronous). Basically, this proves to us that you can have a Promise that resolves another Promise and you can handle them one after another by chaining control methods like `then`, `catch` and `finally`.

## async/await

Chaining `.then()`, `.catch()` and `.finally()` can be sometimes painful and lead to the creation of hard-to-read code. ES8 (or EcmaScript 2017) introduced some _syntax sugar_ for easier handling promises - **async** and **await**. Let's rewrite our Charmander example using async/await.

```javascript
(async () => {
  const response = await fetch('https://pokeapi.co/api/v2/pokemon/charmander');

  try {
    if (response.status === 200) {
      const charmanderData = await response.json();
      console.dir(charmanderData);
    } else {
      throw new Error(response.statusText);
    }
  } catch (error) {
    console.error(error);
  }
})();
```

This code does exactly the same which is done by the previous code - it's only written in a different way. We can't use **await** outside of asynchronous functions, so we are bypassing it by creating a self-calling async function. Inside this function, we are waiting for the response returned by `fetch()`. After we receive the response we are going to check its status code, when it's OK we await for the response body to be parsed and after that, we are going to output it. You probably noticed the missing of `.catch()`. We replaced it with a try-catch block, basically, it is going to do the same thing as `.catch()`. If anything inside `try` throws an error the code will stop to execute and the error handling code inside `catch` will be run instead.

I mentioned async functions and that await can be used only inside them. It is a new type of functions introduced in ES8, and, simplifying, it's a function that utilizes Promise-based behavior, which means that an async function always returns a Promise. It can be then awaited in another async function or treated like a Promise.

```javascript
async function getCharmanderData() {
  const response = await fetch('https://pokeapi.co/api/v2/pokemon/charmander');
  return response.json();
}

(async () => {
  console.dir(await getCharmanderData());
})();
```

> **NOTE.** This example was simplified, and no exception handling is now included.

We moved our logic that is responsible for fetching Charmander's data from [pokeapi.co](https://pokeapi.co/) to an async function. After this, every time, when we need that data we can simply call this function with await and we can deal with it without writing long promise chains.

I said that an async function can be treated like a Promise, and here is an example of how we can do this.

```javascript
async function getCharmanderData() {
  const response = await fetch('https://pokeapi.co/api/v2/pokemon/charmander');
  return response.json();
}

getCharmanderData().then((data) => {
  console.dir(data);
});
```

Await can also be used on normal functions that return a Promise.

```javascript
function delay(time) {
  return new Promise((resolve) => {
    setTimeout(resolve, time);
  });
}

(async () => {
  console.info('Start!');
  await delay(5000);
  console.info('5 seconds have passed.');
})();
```

## Promise helpers

The **Promise** object has also some pretty useful methods that can help us with handling many Promises.

### Promise.all()

`Promise.all()` awaits for all passed Promises to be fulfilled and _resolves_ all results to an array.

```javascript
const charmander = fetch('https://pokeapi.co/api/v2/pokemon/charmander').then((response) => response.json());
const bulbasaur = fetch('https://pokeapi.co/api/v2/pokemon/bulbasaur').then((response) => response.json());
const squirtle = fetch('https://pokeapi.co/api/v2/pokemon/squirtle').then((response) => response.json());

Promise.all([charmander, bulbasaur, squirtle]).then((result) => {
  console.dir(result);
});
```

Worth mentioning is the fact, that when one of the passed promises is rejected `Promise.all()` is also rejected.

### Promise.allSettled()

It's similar to `Promise.all()` but it's not _rejected_ when one (or more) of the passed promises is _rejected_.

```javascript
const charmander = fetch('https://pokeapi.co/api/v2/pokemon/charmander').then((response) => response.json());
const fail = fetch('https://pokeapi.co/api/v2/pokemon/non-existing').then((response) => response.json()); // This Promise is going to fail
const squirtle = fetch('https://pokeapi.co/api/v2/pokemon/squirtle').then((response) => response.json());

Promise.allSettled([charmander, fail, squirtle]).then((result) => {
  console.dir(result);
});
```

### Promise.any()

`Promise.any()` is fulfilled when any of the passed Promises is fulfilled. It is also going to return the result of the first resolved **Promise**. When none of the passed promises is fulfilled `Promise.any()` is going to be rejected.

```javascript
const charmander = fetch('https://pokeapi.co/api/v2/pokemon/charmander').then((response) => response.json());
const bulbasaur = fetch('https://pokeapi.co/api/v2/pokemon/bulbasaur').then((response) => response.json());
const squirtle = fetch('https://pokeapi.co/api/v2/pokemon/squirtle').then((response) => response.json());

Promise.any([charmander, bulbasaur, squirtle]).then((result) => {
  console.dir(result);
});
```

### Promise.race()

It is resolved when any of the passed promises is resolved or rejected.

```javascript
const charmander = fetch('https://pokeapi.co/api/v2/pokemon/charmander').then((response) => response.json());
const bulbasaur = fetch('https://pokeapi.co/api/v2/pokemon/bulbasaur').then((response) => response.json());
const squirtle = fetch('https://pokeapi.co/api/v2/pokemon/squirtle').then((response) => response.json());

Promise.race([bulbasaur, charmander, squirtle]).then((result) => {
  console.dir(result);
});
```

-----

Now you should have a better understanding of JavaScript's asynchronicity. As homework try to play with [pokeapi.co](https://pokeapi.co/) and the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch). Create custom Promises that are going to fetch Pokemons after a certain delay or Fetch data based on something you received in an earlier Promise. You can also use async/await and Promise helpers in your code to experiment even more with this topic. See you (or read you?) and happy coding!

P.S. If you like my work remember to checkout [my blog](https://blog.danieo.me/) and consider signing up for my newsletter (only good content, no SPAM, I **Promise** 😃)