← Back to JavaScript Course | Chapter 4: Modern JS, Async & DOM | Lesson 17 of 26

JS Async/Await

Think of following a recipe step by step, wait for the water to boil, then add the pasta, then wait again before draining it, each step reads naturally in order, even though some steps genuinely take time to finish. async and await let asynchronous JavaScript code read that same natural, top-to-bottom way, instead of chaining .then() calls, you write await before a promise and the code pauses right there until it's ready, without freezing the rest of the page. This makes asynchronous logic, like fetching a tutorial's data on cookiescursor.com, dramatically easier to read and reason about.

The async Keyword

Placing async before a function declaration marks it as asynchronous, allowing the use of await inside it, and automatically making the function itself return a promise, even if it looks like it returns a plain value.

Note: Any function that needs to use await, anywhere inside it, must itself be declared with the async keyword.

Warning: An async function always returns a promise, even a simple return text; inside it actually returns a promise that resolves to that text.

Example: The async Keyword

javascript
async function getValue() {
  return 42;
}
getValue().then(v => console.log(v)); // async function returns a promise

The await Keyword

await pauses an async function's execution at that exact line until the promise it's waiting on settles, then resumes with the resolved value, letting asynchronous code read in a natural, sequential order.

Note: await only pauses the async function it's inside, the rest of the page and any other code continues running normally in the meantime.

Warning: Using await on a value that isn't a promise still works, but it simply resolves immediately, it's not an error, just unnecessary in that case.

Example: The await Keyword

javascript
async function run() {
  const value = await Promise.resolve(10);
  console.log(value);
}
run();

Error Handling With try/catch

Wrapping await statements in a try block lets you catch a rejected promise's error in the matching catch block, providing a clean, familiar way to handle asynchronous errors without chaining .catch().

Note: try/catch around await reads very similarly to handling errors in regular synchronous code, which is one of async/await's biggest readability advantages.

Warning: An await inside a try block that isn't actually followed by a matching catch still throws normally, uncaught errors in async functions can silently fail elsewhere.

Example: Error Handling With try/catch

javascript
async function run() {
  try {
    await Promise.reject("failed");
  } catch (err) {
    console.log("Caught:", err);
  }
}
run();

async/await vs Promises

async/await is built directly on top of promises, it's essentially a more readable syntax for the same underlying mechanism, which is why the two approaches can be mixed freely, and why understanding promises first makes async/await easier to grasp.

Note: For simple, single-step asynchronous code, either style works well, async/await tends to shine most clearly once you have several sequential steps.

Warning: Since async/await is built on promises, any function you await must still actually return a promise, awaiting a non-promise function doesn't make it asynchronous.

Example: async/await vs Promises

javascript
// Same result, two styles:
Promise.resolve(5).then(v => console.log(v));

async function run() {
  const v = await Promise.resolve(5);
  console.log(v);
}
run();

A Practical Example

Combining async/await with try/catch and multiple sequential steps shows the real strength of this pattern, complex asynchronous logic reads almost like straightforward, synchronous code.

Note: When several async steps genuinely don't depend on each other, use Promise.all with await together rather than awaiting them one at a time unnecessarily.

Warning: Awaiting independent operations sequentially, one after another, when they could run simultaneously, unnecessarily slows down your code.

Example: A Practical Example

javascript
async function getUser() {
  try {
    const id = await Promise.resolve(1);
    const name = await Promise.resolve("Sam");
    console.log(id, name);
  } catch (err) {
    console.log("Error:", err);
  }
}
getUser();
Common Mistakes
  1. Using await outside of a function marked async, which causes a syntax error, await only works inside async functions.
  2. Forgetting try/catch around an await, an unhandled rejected promise inside an async function throws an error that needs catching.
  3. Awaiting promises one after another unnecessarily when they don't depend on each other, wasting time that Promise.all could have run in parallel.
Chapter Summary
  • The async keyword marks a function as asynchronous, letting it use await and automatically making it return a promise.
  • await pauses execution within an async function until a promise settles, without blocking the rest of the page.
  • try/catch is the standard way to handle errors from awaited promises inside an async function.
Browser Support

async/await has been supported in all major browsers since 2017 and is now a standard, widely used part of modern JavaScript.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.