ReviseAlgo Logo

Asynchronous JavaScript

async / await

Master modern asynchronous JavaScript. Learn the async/await syntax, syntactic sugar over Promises, execution flow, and sequencing.

Last Updated: July 15, 2026 10 min read

1. Introduction

ES2017 introduced async / await as syntactic sugar on top of Promises. It allows developers to write asynchronous, non-blocking code that looks and behaves like synchronous code, making complex control flows easier to read and maintain.

2. Why It Matters

Using raw Promises can result in complex .then() nesting chains. The async/await syntax flattens these chains, allows the use of standard language control structures (like try/catch or for loops), and improves stack trace readability during debugging.

3. Real-World Analogy

Think of a Smart Navigation App:

  • Promise Chains (.then): Reaching a junction, stopping, reading a signpost, deciding the next turn, driving forward, and repeating at the next junction. The process is divided into separate, chained steps.
  • async / await: Flipping on autopilot. You simply sit back and watch the car drive down the road. Even though the navigation system is executing complex calculations and waiting on traffic signals in the background, your journey feels like a single, continuous forward drive.

4. How It Works

The syntax consists of two keywords:

1. The async Keyword:

Placed before a function declaration. An async function always returns a Promise. If the function returns a value, the engine automatically wraps it in a resolved Promise.

2. The await Keyword:

Used only inside an async function. It pauses function execution until the awaited Promise settles, returning the resolved value or throwing the rejection error. While the function is paused, the main execution thread is released, allowing other tasks to run.

5. Sequential vs Parallel Execution

A common mistake when using async/await is executing independent requests sequentially instead of in parallel:

6. Practical Example

This script demonstrates executing a sequence of dependent tasks using async/await:

7. Common Mistakes

  • Unintentional Sequential execution: Placing await on independent requests immediately, which prevents them from running in parallel.
  • Forgetting to handle rejections: If an awaited Promise rejects, the function throws an error. If not wrapped in a try/catch block, the error becomes an uncaught exception, which can crash the application.

8. Quick Quiz

Q1: What does an async function return if you return a plain string value?

A) A string

B) A Promise that resolves to that string

Answer: B — Async functions always return a Promise. Any returned value is wrapped in a resolved Promise automatically.

9. Scenario-Based Challenge

The Parallel Asset Preloader:

An application preloads 5 assets. You have an async preloader function: preloadAsset(url). Write a function that triggers all 5 preloads in parallel and waits for all of them to finish before returning success.

10. Debugging Exercise

Explain why this code crashes, and how to fix it:

function getSetup() {
  // Objective: Fetch configuration port
  const res = await fetch('/setup.json'); // syntax bug!
  return res.json();
}
getSetup();
View Solution

Diagnosis: The await keyword can only be used inside functions declared with the async modifier (except for top-level await in modules). Using it in standard functions throws a SyntaxError.

Fix: Add the async keyword to the function declaration:

async function getSetup() {
  const res = await fetch('/setup.json');
  return res.json();
}

11. Interview Questions

🟢 Q1: Explain how the JavaScript Engine pauses execution during an await statement without blocking the main thread.

Answer: When the engine hits an await statement, it suspends execution of the async function and saves its context (variable bindings and execution state) in the heap. It pops the function from the Call Stack, releasing the main thread to process other tasks (like user events or rendering). Once the awaited Promise resolves, a microtask is queued to resume the function. The engine then restores its context to the Call Stack and continues execution from where it was paused.

12. Production Considerations

  • Sequential Bottlenecks: When refactoring code to use async/await, look for independent requests that are being awaited sequentially. Group them using Promise.all to run them in parallel and improve performance.