Asynchronous JavaScript Under 5 Minutes
Tags: javascript, asynchronous, beginners
Published: 2019-08-11 03:54:06

&nbsp;

JavaScript makes use of callbacks, promises, async and await features to support [Asynchronous Programming](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Concepts). We won't dive into too many details with each topic, but this article should be a gentle introduction to get you started. Let's commence!

&nbsp;

## Example setup

Take a look at this simple example. We have an initial array with pre-filled numbers, 'getNumbers' function which loops over the array and outputs each item in the array and the 'addNumber' function to receive a number and add it to the array.

```javascript
const numbers = [1, 2];

function getNumbers() {
  numbers.forEach(number => console.log(number))
}

function addNumber(number) {
  numbers.push(number);
}

getNumbers(numbers) // 1, 2
addNumber(3);
getNumbers(numbers) // 1, 2, 3
```

&nbsp;

## The problem

Now, let's suppose both of our function calls take some amount of time to execute because we are making requests to a backend server. Let's mimic it by using built-in setTimeout methods and wrap our logic inside them.

```javascript
const numbers = [1, 2];

function getNumbers() {
  setTimeout(() => {
    numbers.forEach(number => console.log(number))
  }, 1000)
}

function addNumber(number) {
  setTimeout(() => {
  numbers.push(number)
  }, 2000)
}

getNumbers(numbers) // 1, 2
addNumber(3)
getNumbers(numbers) // 1, 2 ... Why?
```
Take a look at the console now. It's behaving differently than before. This is because the 'addNumber' function takes 2 seconds to run and the 'getNumbers' function takes a second to run. Therefore, 'addNumber' function gets executed after two of our 'getNumbers' get called. 'addNumber(3)' function call won't wait for its previous line to finish.

&nbsp;

## Callbacks

Invoking asynchronous calls line by line won't work in this case. Is there any other way to make sure a function gets executed **only after** another function finishes executing? Callbacks can help us! In javascript, functions can get passed around as arguments. Therefore, we could have 'getNumbers' function get passed into addNumber function and execute it once a number has been added.

```javascript
const numbers = [1, 2];

function getNumbers() {
  setTimeout(() => {
    numbers.forEach(number => console.log(number))
  }, 1000)
}

function addNumber(number, callback) {
  setTimeout(() => {
  numbers.push(number)
  callback();
  }, 2000)
}

getNumbers(numbers) // 1, 2
addNumber(3, getNumbers) // 1, 2, 3
```

Here is the flow of our codebase. 'getNumbers' is invoked after 1 second. 'addNumbers' is invoked after 2 seconds (1 second after 'getNumbers'). After it pushes the number to the array, it calls 'getNumbers' again, which takes an additional 1 second. The program fully terminates after 3 seconds. To learn more about callbacks, I wrote an [article](https://dev.to/shimphillip/call-me-maybe-callbacks-for-beginners-k60) in-depth before.

&nbsp;

## Promises

Here is the rewrite of the same code. We will no longer use a callback and call it directly so let's modify our 'addNumber' function to not take in the 2nd argument anymore. Instead, it will return a promise using `new Promise()` keyword immediately. A promise is able to use resolve and reject, given from arguments that you can call after certain actions. If everything goes well, you can call resolve().

&nbsp;

```javascript
const numbers = [1, 2];

function getNumbers() {
  setTimeout(() => {
    numbers.forEach(number => console.log(number))
  }, 1000)
}

function addNumber(number) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      numbers.push(number);
      resolve();
    }, 2000)
  });
}

addNumber(3).then(getNumbers) // 1, 2, 3 after 3 seconds
```

When the promise is actually returned, we can chain it by using `then` keyword. You can then pass in a function definition to be called after your promise is resolved! Awesome! However, what if there was an error such as a network timeout? We could use the reject keyword and indicate that an action was unsuccessful. Let's manually reject it.

```javascript
const numbers = [1, 2];

function getNumbers() {
  setTimeout(() => {
    numbers.forEach(number => console.log(number))
  }, 1000)
}

function addNumber(number) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      numbers.push(number);
      const isAdded = false;
      if (isAdded) {
        resolve();
      } else {
        reject("There was an error")
      }
    }, 2000)
  });
}

addNumber(3).then(getNumbers).catch((e) => console.log(e)) // There was an error
```

Notice that we can pass in a string which is caught by using `.catch` and is available via its first argument. We could do the same thing with the resolve method as well by passing in some data and receive it inside the `then()` method.

&nbsp;

## Async & Await

Let's take the same code and use async and await! Here is a spoiler! We will still need to return a promise, but the way we handle it is different. Take a look.

```javascript
const numbers = [1, 2];

function getNumbers() {
  setTimeout(() => {
    numbers.forEach(number => console.log(number))
  }, 1000)
}

function addNumber(number) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      numbers.push(number);
      const isAdded = true;
      if (isAdded) {
        resolve();
      } else {
        reject("There was an error")
      }
    }, 2000)
  });
}

async function initialize() {
  await addNumber(3);
  getNumbers();
}

initialize(); // 1, 2, 3
```

Instead of chaining then and catch to the addNumber invocation, we created a function called initialize. Using the 'await' keyword requires its wrapper function to have 'async' keyword prepended. Also, the 'await' keyword makes our code more intuitive to reason about because our code reads line by line now even though it's async! 

Now, How about error handling?

```javascript
const numbers = [1, 2];

function getNumbers() {
  setTimeout(() => {
    numbers.forEach(number => console.log(number))
  }, 1000)
}

function addNumber(number) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      numbers.push(number);
      const isAdded = false;
      if (isAdded) {
        resolve();
      } else {
        reject("There was an error")
      }
    }, 2000)
  });
}

async function initialize() {
  try {
    await addNumber(3);
    getNumbers();
  } catch (e) {
    console.log(e);
  }
}

initialize(); // There was an error
```

Let's use try and catch inside our initialize function. If a promise is rejected, our catch block will run.

&nbsp;

## Summary

We learned a few different ways on how to handle different methods of handling asynchronous JavaScript. As for me, I personally prefer writing async and await at all times of how easy it is to write and think about. But others have their places especially callbacks as some APIs only support them. Thank you for reading and let's write some serious code with our newly acquired knowledge!

*This example code was inspired by Brad Traversy's youtube [video](https://www.youtube.com/watch?v=PoRJizFvM7s).*

&nbsp;