Async/Await in JavaScript

Async/Await in JavaScript

In this article, you will learn about Async/Await in javaScript, what it really is, and how it works behind the scenes. using-async-await-expressjs.jpeg What is Async/Await?

Async/Await is nothing but a syntactical sugar to work with promises in a more comfortable fashion. It’s surprisingly easy to understand and use than Promises.

Async/Await attempts to solve one of the biggest pains in the language since its beginning that is synchrony.

They allow you to write promise-based code as if it were synchronous, but without blocking the main thread. They make your asynchronous code less mouthful and more readable.

Before I start with Async/Await, I would suggest you take a look at my article on Promises in JavaScript to understand the Promises first. Because you need to understand Promises before you can understand how to use Async/Await.

ASYNC/AWAIT WORKING & SYNTAX:

const asyncFunc = async () => {
  // to handle errors in async/await we use try and catch block 
  try {
    // we will store only resolved value in resolvedValue variable
    const resolvedValue = await promise;
  }
  catch (rejectedValue) {
    // handle only rejected value (error) here
  }
}

If you use the async keyword in a function definition, then only you can use await within the function.

When you await a promise, the function is paused in a non-blocking way until the promise returns any value.

If the promise resolves, you get the resolved value back. If the promise rejects, the rejected value is thrown which is nothing but an error.

EXAMPLE TO UNDERSTAND THE ASYNC/AWAIT:

What are we doing?

  • Here, we are creating a product and as soon as we create a product we want to fetch all the products that are stored in the database, including newly created product.

  • In case any error occurs we want to reject the promise, catch that rejection in the try and catch block, and instead of crashing our app, we want to simply console log the error without fetching the products.

// Let's assume that this `products` variable is a data that is
// already present in our database
const products = [
  {
    title: "Mobile",
    price: "13999",
  },
  {
    title: "Laptop",
    price: "40999",
  },
  {
    title: "Headphones",
    price: "3999",
  },
];

const createProduct = (product) => {
  return new Promise((resolve, reject) => {
    // here setTimeout is immitating time require to fetch data/api from server
    setTimeout(() => {
      // lets say there is no error
      let error;
      if (product) {
        // if we receive a product that means there is no error
        error = false;
        // so we will push the received product in our existing products' array
        products.push(product);
      } else {
        // if we don't receive a product that means there is some error
        error = true;
      }
      if (!error) {
        resolve("Created the product successfully");
      } else {
        reject("Some error occurred");
      }
    }, 3000);
  });
};

let fetchProducts = () => {
  console.log("fetching the products...\n");
  // here setTimeout is immitating time require to fetch data/api from server
  setTimeout(() => {
    products.forEach((prod) => {
      console.log(prod.title);
    });
  }, 2000);
};

// asynv function
const asynFunc = async () => {
  // try and catch for error handeling
  try {
    // we will store the 'resolved' value in productsMsg
    let productsMsg = await createProduct({
      title: "new product",
      price: "399",
    });
    console.log(productsMsg);
    fetchProducts();
  } catch (error) {
    // we will get 'rejected' value as an error
    console.log(error);
  }
};

asynFunc();

console.log("next lines of code\n");

When we call asyncFunc() which is an async function, it will wait until createProduct() promise returns a resolved or rejected value (Depending on the value of the error variable).

If the promise is resolved then only it will fetch the products by executing fetchProducts(), otherwise it will throw an error by rejecting the promise.

When you run the above code you will get the following output:

If we receive the product, that means error = false:

async4.PNG

When we pass the product in createProduct(), the error becomes false. It will return the resolved value (after 3 seconds). We will store that resolved value in the productMsg variable and after that, we will console log that value/message, and also, we will fetch all the products by calling the fetchProducts() function.

If we don't receive the product, that means error = true:

async5.PNG When we don't pass the product in createProduct(), the error becomes true. It will return the rejected value (after 3 seconds). We will catch that rejected value in the catch () {} block and we will console log that error message. This is how the error handling happens in async/await.

REAL WORLD USE CASE USING axios():

Say we wanted to fetch a URL and console log the response. Here's how it looks using Promises:

const fetchUrl = (url) => {
  return axios
    .get(url)
    .then((response) => {
      console.log(response.data);
    })
    .catch((err) => {
      // Handle Error Here
      console.error(err);
    });
};

And here's the same thing using async/await functions:

const fetchUrl = async (url) => {
  try {
    const response = await axios.get(url);
    console.log(response.data);
  } catch (err) {
    // Handle Error Here
    console.error(err);
  }
};

If you notice, unlike promises in async/await function all the callbacks are gone. This makes it easier to read, especially for those who are not that familiar with promises.

DO THINGS IN PARALLEL:

Even though you're writing code that looks synchronous, ensure you don't miss the opportunity to do things in parallel.

The following code will take 15 seconds to complete: (Not recommended)

const asyncFunc = async() => {
  await promise1(5000); // promise1 will take 5 seconds to complete
  await promise2(5000); // promise2 will take 5 seconds to complete
  await promise3(5000); // promise3 will take 5 seconds to complete

  // after 15 seconds it will return "done!"
  return "done!";
}

Now, take a look at the following code: (Recommended)

const asyncFunc = async() => {
  // following promises will start a 5 seconds timer asynchronously/parallelly
  const prom1 = promise1(5000);
  const prom2 = promise2(5000);
  const prom3 = promise3(5000);
  // when you declare promises using the above method
  // all the promises will run parallelly
  await prom1;
  await prom2;
  await prom3;

  // after 5 seconds it will return "done!", which is much much faster.
  return "done!";
}

Async/Await makes execution sequential. It's not necessarily a bad thing, but having paralleled execution is much faster.

CONCLUSION:

  • Before Async/Await functions, JavaScript code that relied on lots of asynchronous events (using callbacks) would end up in what some called “callback hell” which is a chain of functions and callbacks that was very difficult to read and understand.

  • Async/Await allows us to write asynchronous JavaScript code that reads much more clear, efficient, and easy to understand. Also, it makes sure that the function will always return a promise.

  • With Async/Await we rarely need to write promise.then().catch(), but we still shouldn’t forget that they are based on promises.

I hope you have learned something new! If you found this article useful, be sure to share, follow and support!

Thank you for reading!

Also, Make sure to subscribe our newsletter on blog.learncodeonline.in and never miss any upcoming articles related to programming just like this one.

I hope this post will help you in your journey. Keep learning!

My LinkedIn and GitHub .

Did you find this article valuable?

Support Learn Code Online by becoming a sponsor. Any amount is appreciated!