Promises and async/await

HighHardJavascript
Preparing for interviews?

Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.

Quick Answer

Promises represent a value that may be available now, later, or never. async/await is syntax sugar over Promises that makes asynchronous code look synchronous, while preserving microtask scheduling and error propagation via rejections.

Answer

The Core Idea

A Promise is a container for an eventual result: pending → fulfilled (resolved) or pending → rejected. async/await makes working with Promises easier: await pauses within an async function until the Promise settles, then returns the value or throws the rejection.

Concept

What it means

Example

Promise states

pending → fulfilled / rejected

new Promise((res, rej) => ...)

Thenables

Anything with .then behaves like a Promise

Libraries often return thenables

Microtasks

Promise callbacks (.then/.catch/.finally) run as microtasks

Run after current stack, before timers

async

Marks a function that always returns a Promise

async function f(){ return 1 }

await

Pauses inside async until the Promise settles

const data = await fetch(...)

Promises and async/await at a glance.
JAVASCRIPT
// Basic Promise
function getUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve({ id, name: 'John' }), 200);
  });
}

getUser(1)
  .then(u => u.name)
  .then(name => console.log(name))
  .catch(err => console.error('Error:', err))
  .finally(() => console.log('done'));
                  
JAVASCRIPT
// Equivalent with async/await
async function main() {
  try {
    const u = await getUser(1); // waits for fulfillment
    console.log(u.name);
  } catch (err) {
    console.error('Error:', err); // rejections become throw
  } finally {
    console.log('done');
  }
}
main();
                  

Sequential vs Parallel

Using await inside a loop can serialize work unintentionally. Create Promises first, then await Promise.all for parallelism.

JAVASCRIPT
// ❌ Sequential (slow)
for (const url of urls) {
  const res = await fetch(url);
  results.push(await res.json());
}

// ✅ Parallel (fast)
const promises = urls.map(url => fetch(url).then(r => r.json()));
const results = await Promise.all(promises);
                  

Promise combinators

API

Settles When

Behavior

Promise.all([...])

All fulfill or one rejects

Fast-fail; rejects on first rejection

Promise.allSettled([...])

All settle

Never rejects; returns statuses

Promise.race([...])

First settles

Adopts winner's state/value

Promise.any([...])

First fulfillment

Rejects only if all reject (AggregateError)

Choosing the right combinator.

Error handling

With Promises, use .catch or the second arg of .then. With async/await, use try/catch. Unhandled rejections can crash Node.js (depending on version) or show as console warnings in browsers.

JAVASCRIPT
// Handling errors: async/await
async function load() {
  try {
    const data = await fetch('/api').then(r => {
      if (!r.ok) throw new Error('HTTP ' + r.status);
      return r.json();
    });
    return data;
  } catch (e) {
    // handle or rethrow
    throw e;
  }
}
                  

Common pitfalls

  • Forgetting to return inside .then chains → next .then gets undefined.
  • Using await in Array.prototype.forEach (it won’t await). Prefer for...of or Promise.all.
  • Assuming async functions throw synchronously — they return a rejected Promise instead.
  • No built-in cancellation for Promises (use AbortController with fetch or libraries that support cancel).
JAVASCRIPT
// from callback to Promise
function readFileP(path) {
  const fs = require?.('fs'); // works in Node
  return new Promise((resolve, reject) => {
    fs.readFile(path, 'utf8', (err, data) => {
      if (err) reject(err); else resolve(data);
    });
  });
}
                  
Still so complicated?

Think of a Promise as an order ticket 🧾: you place the order (create Promise), you’ll be notified when it’s ready (microtask). With await, you stand by the counter for just that ticket without blocking other customers — when it’s ready, you pick it up and continue.

Summary
  • Promises model eventual results and schedule callbacks as microtasks.
  • async/await is Promise syntax sugar: clearer flow, same semantics.
  • Prefer parallel with Promise.all; avoid accidental serialization.
  • Use try/catch (or .catch) and pick the right combinator for your needs.
Similar questions
Guides
14 / 61