100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Node.js

Async/Await in Node.js

Writing asynchronous code that reads like synchronous code using the async/await syntax built on Promises.

Asynchronous ProgrammingIntermediate9 min readJul 8, 2026
Analogies

Introduction

async/await is syntactic sugar built on top of Promises that lets you write asynchronous code in a linear, synchronous-looking style. An async function always returns a Promise. Inside an async function, the await keyword pauses execution of that function (without blocking the rest of the program) until the awaited Promise settles, then resumes with the fulfilled value or throws the rejection reason as an exception.

🏏

Cricket analogy: await is like a non-striker batsman waiting at the crease for the umpire's signal before running — the rest of the ground keeps functioning, but that specific play pauses until the delivery is resolved.

Syntax

javascript
async function getData() {
  try {
    const value = await somePromiseReturningFunction();
    console.log(value);
    return value;
  } catch (err) {
    console.error('Caught error:', err.message);
    throw err; // optional: re-throw for the caller to handle
  }
}

getData().then((v) => console.log('Resolved with', v));

Explanation

Because await only works with Promises (or thenables), and an async function's return value is automatically wrapped in a Promise, async/await is fully interoperable with existing Promise-based code — it does not replace Promises, it just provides better ergonomics on top of them. Error handling uses standard try/catch instead of .catch(): if an awaited Promise rejects, the rejection is thrown as an exception at the await expression, which a surrounding try/catch can intercept. Without a try/catch, the rejection instead causes the async function's returned Promise to reject.

🏏

Cricket analogy: Using try/catch around await is like a fielder wearing a helmet at short leg — if the ball takes an unexpected edge, the helmet (catch block) absorbs the shock instead of the mishap ending the innings entirely.

Example

javascript
function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id > 0) resolve({ id, name: 'Alice' });
      else reject(new Error('Invalid id'));
    }, 300);
  });
}

function fetchOrders(userId) {
  return new Promise((resolve) => setTimeout(() => resolve(['order1', 'order2']), 300));
}

async function loadDashboard(id) {
  try {
    const user = await fetchUser(id);
    const orders = await fetchOrders(user.id);
    console.log(`${user.name}'s orders:`, orders);
  } catch (err) {
    console.error('Failed to load dashboard:', err.message);
  }
}

loadDashboard(42);

// Running independent awaits concurrently
async function loadBoth() {
  const [u1, u2] = await Promise.all([fetchUser(1), fetchUser(2)]);
  console.log(u1, u2);
}

Output

loadDashboard(42) logs "Alice's orders: [ 'order1', 'order2' ]" after roughly 600ms (300ms for fetchUser, then 300ms for fetchOrders sequentially, because the second await waits for the first to finish). If id were invalid, fetchUser's rejection would be caught by the catch block, logging 'Failed to load dashboard: Invalid id'. In loadBoth(), wrapping independent awaits in Promise.all runs them concurrently instead of sequentially, finishing in ~300ms total instead of ~600ms.

🏏

Cricket analogy: Batting first then bowling second sequentially in an exhibition takes far longer than running two simultaneous net sessions on separate pitches — Promise.all is like using two nets at once to finish training faster.

Key Takeaways

  • async functions always return a Promise, even if you return a plain value.
  • await pauses only the current async function, not the whole program (the event loop keeps running).
  • await can only be used with a value that is a Promise (or thenable) meaningfully, and only inside async functions (or top-level modules).
  • Use try/catch around await to handle rejections; unhandled rejections propagate as a rejected Promise from the async function.
  • Sequential awaits run one after another; use Promise.all to run independent async operations concurrently for better performance.

Practice what you learned

Was this page helpful?

Topics covered

#NodeJs#NodeJsExpressStudyNotes#WebDevelopment#AsyncAwaitInNodeJs#Async#Await#Node#Syntax#Concurrency#StudyNotes#SkillVeris