Asynchronous programming in JavaScript - Promises, Callbacks and Async-await
Tags: javascript, node, programming, tutorial
Published: 2021-12-10 11:53:02

## Asynchronous programming in JavaScript

> Understanding asynchronous programming in JavaScript is fundamental to using JavaScript. Asynchronous means several operations occurring at the same time.

This guide provides and a simple introduction to asynchronous programming in JavaScript. It covers the basics and not everything there is to asynchronous programming in JavaScript.

Fork, clone or download sample project here [sample project](https://github.com/naftalimurgor/rx-coin-market-cap)

Remix the project at [glitch.io](https://glitch.com/edit/#!/bald-shadowed-racer)

JavaScript was initially developed to add interactivity to html elements on a page. For instance, when a page loads, the JavaScript gets loaded then parsed. A button on the page sits there waiting for a `click` mouse event. We attach a callback function to the event to be triggered when the click `event` fires.

```typescript
const loginBtn = document.getElementById('login-btn')
loginBtn.addEventListener('click', () => {
  // do something when 'click' mouse event fires i.e button      // is clicked
})
```

> Asynchronous programming is when two or several operations happen at the same time. In JavaScript, asynchronous programming is highly used and a good grasp is ideal.

Let's say one has a page that displays the coin market cap(Price and volume) of various crypto. You'd asynchronously fetch the data from an API, while the page continues to render during page load. Once the results become available, we render the results on the webpage.

JavaScript offers three ways of performing asynchronous actions:

- Using callbacks
- Using Promises
- Async-await - Most recent development introduced in ES7 version

### 1. using callbacks

callbacks are functions passed to other functions as values. They are "inline" functions with standard function signature and arguments. They can be `arrow functions` or `ES5 functions`.

```typescript
// A simple signature of a callback
const waitUp = (someArgs, callback) => {
  setTimeout(() => {
    // mimick a delay, e.g fetching data from an api
    const fakeData = {
      user: 'Kakashi sensei',
      age: 27,
      village: 'Hidden Leaf',
      leadership: '4th Hokage'
    }

    // pass the data to the callback function argument, we will provide this when we call waitUp later in our program

    callback(fakeData) // we then can handle the data we got inside this callback
  }, 3000)
}

// consuming the callback and handling the data asyncronously returned by waitUp

waitUp('', (data) => {
  console.log(data) // our data is now available for use
})
```

Callbacks are common in Nodejs, latest versions of Nodejs provide ES6 promises which are more cleaner to use.

### 2. using promises

Promises are a new standard introduced in the `ES6(ES2015)` version. Promises represent proxy values that are yet to resolve.

When consuming a promise, promises exist in three states:

- pending state
- resolved state
- rejected state

While performing operations that do not resolve immediately such as fetching data from a web API or reading file contents from a disk, results from the operation won't immediately be available for use within your program. Promises make it less painful to perform such operations.

```typescript
// creating a promise, note the new syntax

const waitUp = () =>
  return new 
   Promise((resolve, 
  reject) => {
  // do some operations that won't returns a valu
    setTimeout(() => {
    // mimick a delay, e.g fetching data from and api
    const fakeData = {
      user: 'Kakashi sensei',
      age: 27,
      village: 'Hidden Leaf',
      leadership: '4th Hokage'
    }

    // pass the data to the callback function parameter, we will provide this when we call waitUp later in our program
    resolve(fakeData) // we finally resolve with a value once we get the data
  }, 3000)
})

// consuming the promise created
  waitUp()
    .then((data) => {
      // do something with the data
    })
    .catch((err)=> {
    // handle the promise rejection
    })
```

> Consuming a promise is a matter of chaining the function call with a `.then(() => {})` You may chain as many "dot-thens" to the function call as possible.

However, using promises quicky becomes convoluted and leads to code that is hard to follow as the number of "dot-thens" become hard to follow.

Fetch API uses promises as we shall see. [Fetch API](https://www.w3schools.com/js/js_api_fetch.asp) provides a cleaner way of making HTTP request from the browser. No more `XMLHttpRequest`

```typescript
fetch('http://heroes.glitch.io')
  .then((res) => res.json()) // parses the body into JavaScript object literal
  .then((data) => console.log(data))
  .catch((err) => console.log(err)) // .catch comes last to catch handle any errors when the promise  returns an error
```

In most cases, consuming a promise would be more common especially when making HTTP requests
using a library like `axios` and other HTTP tooling and making network calls.

### 3. async-await

Async-await is a syntactical sugar for promises that was introduced in `ES2017` version to make using promises more cleaner. To use async-await:

1. Declare a function async by adding the `async` keyword to the function signature.

```typescript
// an async function
async function waitUp(args) {

}

// in arrow functions
const waitUp = async(args) => {

}
```

2. To perform any asyncronous calls inside the function / expression you declared async,
   prepend `await` to the call, like:

```typescript
async function waitUp() {
  const res = await fetch('https://glitch.io/heroes')
  const data = await res.json()
  // use the data like in a normal function
  console.log(data)
}

// to handle promise rejections
async function waitUp() {
  try {
    const res = await fetch('https://glitch.io/heroes')
    const data = await res.json()
    // use the data like in a normal function
    console.log(data)
  } catch(ex) {
    // any exceptions thrown are caught here
  }
}
```

> handling rejected promise is as easy as wrapping your asyncronous code inside a try { } catch(ex) { } block
> Async-await makes handling promise cleaner and less painful. You are more likely to see alot of `async-await` in modern JavaScript than promises and callbacks.

Promises and async-await are interoperable, this means what can be done using promises can be done using `async-await`.

For example:
This implementation becomes:

```typescript
const waitUp = new Promise((reject, resolve) => {
  // do some operations that won't return a value immediately
    setTimeout(() => {
    // mimick a delay, e.g fetching data from an api
    const fakeData = {
      user: 'Kakashi sensei',
      age: 27,
      village: 'Hidden Leaf',
      leadership: '4th Hokage'
    }

    // pass the data to the callback function argument, we will provide this when we call waitUp later in our program

    resolve(fakeData) // we finally resolve with a value once we get the data
  }, 3000)
})

// consuming the promise we created above
  waitUp()
    .then((data) => {
      // do something with the data
    })
    .catch((err)=> {
    // handle the promise rejection
    })
```

Becomes:

```typescript
const waitUp = new Promise((reject, resolve) => {
  // do some operations that won't returns a valu
    setTimeout(() => {
    // mimick a delay, e.g fetching data from an api
    const fakeData = {
      user: 'Kakashi sensei',
      age: 27,
      village: 'Hidden Leaf'
      leadership: '4th Hokage'
    }

    // pass the data to the resolve callback

    resolve(fakeData) // we finally resolve with a value once we get the data
  }, 3000)
})

// consuming the promise created using async-await
// assuming a main function somewhere:

const main = async() => {
  const data = await WaitUp()
  // use the data like in a syncronous function call
  console.log(data)
}

main() // calling main
```

## Summary

Understanding the asynchronous aspect of JavaScript is crucial. Constant practise and using promises in a project helps solidify understanding usage of Promises.

Async-await does not replace promises but makes code cleaner and easy to follow. No more `.then(fn)` chains

Follow me on twitter [@nkmurgor](https://twitter.com/nkmurgor) where I tweet about interesting topics.

Do you feel stuck with learning modern JavaScript? You may preorder  [Modern JavaScript Primer for Beginners](https://naftalimurgor.gumroad.com/l/modern-javascript-primer-for-beginners) where I explain everything in a clear and straight-forward fashion with code examples and  project examples.

This article was orignally published at [naftalimurgor.com](naftalimurgor.netlify.com)
Thanks for stopping by!