A Beginner's Guide To Javascript async / await.
Tags: webdev, javascript, beginners, coding
Published: 2023-09-01 10:45:10

Javascript is a powerful programming language used extensively in web development. When building web applications, you often encounter tasks that require fetching data from external sources or executing time-consuming operations. Handling these tasks asynchronously is crucial to ensure your application remains responsive and efficient. One of the essential tools for asynchronous programming in Javascript is async and await keywords. In this guide we'll explore what async and await keywords are, how they work and how to use them effectively in your code.
 

## Table of contents

- [Javascript callbacks](#js-callback)

- [Understanding the meaning of asynchronous](#async-meaning)

- [Javascript promises](#promises)

- [Introducing async and await keywords](#keywords)

- [Benefits of async and await](#benefits)

- [Conclusion](#conclusion)

### Javascript callbacks <a name="js-callback"></a>
Before we understand the async and await keywords, we must know what are callbacks in Javascript. 
A callback is a function that is passed as an argument to another function. It allows a function to call another function and it runs after another function is finished.

```
function myFirst(a){
    document.write(a)
}
function myCalculator(num1, num2, myCallback){
    let sum = num1 + num2;
    myCallback(sum)
}

myCalculator(10, 10, myFirst);
```
In the code above, myFirst is a callback function which is passed as an argument in the myCalculator function.

### Understanding the meaning of asynchronous <a name="async-meaning"></a>
Before diving into async and await , let's briefly understand the meaning of asynchronous in Javascript.

In a synchronous program, code execution happens sequentially. This means one operation must complete before the next one begins. However, when dealing with tasks like data fetching from an API or reading a file or waiting for the operation to be completed can make your application feel slow or unresponsive.

In an asynchronous program it allows your code to execute tasks without waiting for the each task to complete.
Most modern asynchronous Javascript methods don't use callbacks, instead asynchronous programming is solved using promises.

### Javascript promises <a name="promises"></a>
A promise is an object that represents the completion or failure of an asynchronous operation. They provide a way to work with asynchronous code in a more organized and readable manner.
A promise exists in one of these states;

1. Pending - initial state when the promise is created.

2. Fulfilled - the state when the asynchronous operation is successfully completed. The promise holds a result value.

3. Rejected - the state when the asynchronous operation encounters an error or fails. The promise holds an error value.

A promise that is either resolved or rejected is called settled.
The promise methods are;

1. then() - it specifies what should happen when a promise is fulfilled. It takes two callback functions which are optional as arguments, one for fulfillment and one for rejection.

2. catch() - it specifies what should happen when a promise is rejected.

3. finally() - it specifies a callback that should be executed regardless of whether the promise is fulfilled or rejected.

```
let promise = new Promise(function(resolve, reject){
    setTimeout(() => {
        const randomValue = Math.random();

        if (randomValue < 0.5) {
            resolve(`success random value: ${randomValue}`)
        } else {
            reject(`error random value: ${randomValue}`)
        }
    }, 2000)
});

promise
    .then((value) => {
        console.log("Promise resolved:", value)
})
    .catch((error) => {
        console.log("Promise rejected:", error)
})
    .finally(() => {
        console.log("Promise operation completed, regardless of success or failure.")
});
```
**Promise constructor**
A new promise is created using the promise constructor. The constructor takes a single argument which is called the executor function.
The executor function has two parameters which are resolve and reject. They are provided by the promise system to signal the successful resolution or the rejection of the promise.

**setTimeout function**
Inside the executor function, a setTimeout function is used to simulate an asynchronous operation with a 2 second delay. After the delay a random value is generated using **Math.random**.
If the randomValue is less than 0.5, the resolve function is generated indicating a successful resolution of the promise. If the randomValue is greater than 0.5, the reject function is called indicating a rejection of the promise.

**methods**
After the promise is created, it can be consumed using the then(), catch() and finally() methods.
If the promise is successful the then() callback will be executed, if it is rejected the catch() callback will be executed. The finally() callback executes regardless of the promise outcome.

But there is a more better way to write asynchronous code which is using async and await keywords. They make the asynchronous code more readable and easy to manage. They are built on top of Javascript promises.

### Introducing async and await keywords <a name="keywords"></a>
async and await were introduced in ECMAScript 2017(ES8). They take asynchronous programming a step further by providing a synchronous-like syntax.
**async keyword**
The async keyword is for a function that is supposed to perform an asynchronous operation. It is used to define a function that returns a promise.
**await keyword**
The await keyword is used inside an async function to pause the execution until a promise is settled(resolved or rejected). It allows you to write asynchronous code that looks and behaves more like synchronous code making it easier to manage complex asynchronous flows.

```
function fetchData() {
    return new Promise((resolve) => {
        setTimeout(() => {
            const data = {message:"Data fetched successfully"}
            resolve(data);
        },3000)
    })
}
async function fetchDataAsync(){
    try {
        const result = await fetchData()
        console.log(result.message);
    } catch (error){
        console.error("Error fetching data", error);
    }
}

fetchDataAsync();
```
**fetchData function**
The fetchData function returns a promise and inside the promise there is a setTimeout function which simulates a delay of 3 seconds. After the delay, it resolves the promise with an object data containing a message.

**fetchDataAsync**
The fetchDataAsync is an asynchronous function declared with the async keyword. It uses the await keyword to call the fetchData function. It means that the execution of the fetchDataAsync will pause until the promise returned by fetchData is resolved.

If the promise is resolved successfully without errors the result is stored in the result variable then the message is displayed in the console.

The try and catch block is used to handle potential errors that might occur during the asynchronous operation. If there was an error during the promise resolution, the catch block will catch the error and log an error message along which the specific error that occured.

### Benefits of async and await <a name="benefits"></a>

- **Readability** - asynchronous code written with async and await is easier to read and understand compared to nested callbacks or chained promises.

- **Error-handling** - error handling is simplified through try and catch blocks which makes it easier to manage errors.

- **Synchronous-like flow** - asynchronous code written with async and await looks and behaves like synchronous code making it easier for developers to reason about its flow.

### Conclusion <a name="conclusion"></a>
Asynchronous programming is a cornerstone of modern JavaScript development, and async and await are powerful tools that streamline the process. They provide a way to handle asynchronous operations in a synchronous-like manner, resulting in more readable, maintainable, and efficient code.