JavaScript async/await

Introduction

Modern JavaScript has made asynchronous programming much easier and more readable with the introduction of async and await in ES2017 (ES8). These keywords are built on top of Promises and allow developers to write asynchronous code that looks and behaves like synchronous code.

If you’ve ever struggled with chained .then() calls or callback hell, async/await is the clean, readable solution you’ve been looking for.


What Are async and await?

  • async: Declares a function as asynchronous, meaning it automatically returns a Promise.
  • await: Pauses the execution of the async function until the Promise is resolved or rejected.

Together, these allow you to write asynchronous code that reads almost like a story—top to bottom, left to right.


Basic Syntax

Example:

javascriptCopyEditasync function greet() {
  return "Hello";
}

greet().then((message) => console.log(message)); // Output: Hello

Even though greet() looks like a normal function, it returns a Promise because of the async keyword.


Using await

The real power comes when you use await to pause for a Promise:

javascriptCopyEditfunction delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function run() {
  console.log("Start");
  await delay(2000); // Waits for 2 seconds
  console.log("End after 2 seconds");
}

run();

Output:

pgsqlCopyEditStart
(wait 2 seconds)
End after 2 seconds

The function waits for the Promise returned by delay() to resolve before continuing.


Real-World Example: Fetching Data

Here’s how async/await is used in a real application, such as fetching data from an API:

javascriptCopyEditasync function getUser() {
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("Error fetching user:", error);
  }
}

getUser();

Explanation:

  • fetch returns a Promise for the HTTP response.
  • response.json() also returns a Promise.
  • We await both to get the final user data.
  • Errors are handled with try/catch.

Why Use async/await?

Benefits:

  • Cleaner syntax than .then() chains
  • Easier error handling with try/catch
  • More readable and maintainable
  • Works seamlessly with existing Promise-based APIs

Comparison:

With Promises:

javascriptCopyEditfetch(url)
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.error(err));

With async/await:

javascriptCopyEditasync function getData() {
  try {
    const response = await fetch(url);
    const data = await response.json();
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}

Both do the same thing, but the async/await version is more readable.


Sequential vs. Parallel Execution

Sequential:

javascriptCopyEditasync function run() {
  await task1();
  await task2(); // Waits for task1 to finish first
}

Parallel (better performance):

javascriptCopyEditasync function run() {
  const t1 = task1();
  const t2 = task2();
  await t1;
  await t2;
}

If tasks are independent, start them without await and then await them together to improve performance.


Error Handling with try/catch

javascriptCopyEditasync function getData() {
  try {
    const res = await fetch("https://invalid-url");
    const data = await res.json();
    console.log(data);
  } catch (error) {
    console.error("Caught an error:", error);
  }
}

Just like synchronous code, errors can be caught with try/catch, making debugging easier.


Tips & Best Practices

  • Use async/await only inside async functions.
  • Wrap your logic in try/catch blocks to handle errors.
  • Avoid using await in loops when possible—use Promise.all() for parallelism.
  • Don’t block the main thread with long await chains unless necessary.

Conclusion

async/await is one of the most powerful additions to JavaScript for writing cleaner, more readable asynchronous code. It builds on top of Promises but removes much of the complexity associated with chaining and nesting.

Summary:

  • async turns a function into a Promise.
  • await pauses execution until the Promise resolves.
  • Use try/catch to handle errors.
  • It simplifies asynchronous logic significantly.

By mastering async/await, you’ll write more efficient and maintainable JavaScript—whether you’re building APIs, UI apps, or working with databases.