Lesson overview · Free interview practice

Async/Await

Master error handling and parallel execution with async/await.

Topics in the full lesson
  • Introduction to Async/Await
  • Error Handling with Try/Catch
  • Sequential vs. Parallel Execution
  • Best Practices
  • Exercises

Practice Problems

Work through a question before revealing its explanation. These questions and answers are free; Premium adds the full lesson walkthrough, examples and implementation detail.

Completion marks record your own progress, not an automatically checked result. The task type does not determine whether it is optional.

TheoryMedium

async/await Semantics: Desugaring + the Wrapper Promise

Question

What does async actually do to a function? What does await desugar to under the hood? Show the equivalent .then form.

Take a moment to think about this before revealing the answer

Explain your reasoning or try an implementation before comparing answers.

TheoryMedium

`await` in Loops: The forEach Trap + the Parallel-Map Pattern

Question

Find the bug. What does this code actually do, and what are the three correct alternatives?

async function saveAll(items) {
  items.forEach(async (item) => {
    await save(item)
  })
  console.log("all saved")
}

Take a moment to think about this before revealing the answer

Explain your reasoning or try an implementation before comparing answers.

TheoryMedium

Sequential vs Parallel Execution with `async`/`await`

Question

These two functions both fetch three independent resources. How long does each take, and why?

async function sequential() {
  const a = await fetchA()  // 1s
  const b = await fetchB()  // 1s
  const c = await fetchC()  // 1s
  return [a, b, c]
}

async function parallel() {
  const [a, b, c] = await Promise.all([fetchA(), fetchB(), fetchC()])
  return [a, b, c]
}

Take a moment to think about this before revealing the answer

Explain your reasoning or try an implementation before comparing answers.

TheoryMedium

async Functions as Values: Passing, Returning, Composing

Question

async functions are first-class values like any other function. Show how that interacts with HOFs (map, reduce), function composition, and the common gotchas at the boundary.

Take a moment to think about this before revealing the answer

Explain your reasoning or try an implementation before comparing answers.

CodingHard

Async Iteration: `for await...of` and Async Generators

Implement and check your solution in the stated environment before revealing the answer.

Question

Explain what an async iterable is and implement async function* paginate(url, { signal } = {}) that yields paginated API results one page array at a time.

Before you start

This is core practice for async iteration. You need await, fetch, JSON parsing, yield and the iterator protocol. Read Generators if next() and return() are new. Review cancellation in Promise Patterns before using the optional signal.

Use Node 24.21.0, which supplies the runtime APIs used by this fixture. In the companion directory, start the local fixture in its own terminal:

node fixtures/server.mjs

It binds only 127.0.0.1:3055 and 127.0.0.1:3056. If either port is occupied, stop and choose an unused authorized fixture environment rather than reusing another server. Verify the health response identifies async-browser-author-5f46718. The API for this task is:

http://127.0.0.1:3056/pages/normal/1

No external API, credentials or placeholder endpoint is involved. The companion server and check files are part of the exercise setup.

Contract

  • Input is an absolute HTTP or HTTPS URL. Each page has { items: Array, nextUrl: string | null }. The supplied API guarantees a finite, acyclic chain.
  • nextUrl is either null or a nonempty URL string. Resolve relative links against the current page URL. Reject a link that leaves the original origin or HTTP(S).
  • Creating the generator does not start a request. Each next() returns a Promise for an iterator result. Fetch at most one page per pull, with no background prefetch.
  • Yield the entire items array, even if it is empty. Stop only after yielding a page whose nextUrl is null. Do not flatten items into individual yields.
  • Reject the pending next() with Error("HTTP <status>") for a non-OK HTTP response. Propagate fetch and JSON errors. Reject an invalid page shape or disallowed link with TypeError before yielding it.
  • Pass signal to fetch and check for an already-aborted signal before starting a request. A failure closes the generator.
  • An ordinary for await...of loop asks for the next page only after its current body finishes. break closes the iterator, so no later page is fetched. A manually queued return() does not interrupt an already-pending fetch. Use the signal for that.

Local checks

The normal fixture emits arrays of lengths [2, 0, 1], containing Ada, Lin and Sal in that order. Consuming all pages must make exactly three page requests. Breaking after the first must make only one.

In a second terminal, edit tasks/pagination/starter.mjs and run:

CANDIDATE=starter node --test tasks/pagination/check.mjs

Checks also cover relative links, HTTP failure, malformed JSON, invalid shape and abort during a deliberately held response. Stop your fixture with Ctrl-C afterward. Do not terminate an unrelated server.

Starter: tasks/pagination/starter.mjs

export async function* paginate(url, { signal } = {}) {
  throw new Error('Implement lazy page-by-page iteration')
}

Take a moment to think about this before revealing the answer

Explain your reasoning or try an implementation before comparing answers.

CodingHard

Implement a Concurrency Limiter (`mapLimit` / Promise Pool)

Implement and check your solution in the stated environment before revealing the answer.

Question

Implement mapLimit(items, limit, asyncFn). Run at most limit operations in flight and resolve to results in input order, even when operations finish in a different order. Explain why Promise.all(items.map(asyncFn)) does not impose that limit.

Preparation and environment

This is core practice for bounded async work. You need async functions, array indexing, Promise.all, error propagation and the difference between starting work and waiting for it. The sequential and parallel sections of this lesson provide those foundations. Use Node 24.21.0 and the companion files. The checks use manually released Promises, not real services or timing guesses.

Contract

  • items is a dense array. Do not mutate it during the call. asyncFn receives (item, index) and may return a value or thenable, reject, or throw.
  • limit is a positive safe integer. Reject with RangeError for invalid limits, including on empty input. Reject nonarray input or a nonfunction callback with TypeError.
  • Return a native Promise for an array of results. Empty input resolves to [] without calling asyncFn. A limit of 1 is sequential. A limit above the array length starts no more operations than there are items.
  • Start additional work as slots become free. Store each result at the original index. Do not wait for an entire fixed batch before filling an available slot.
  • On the first observed failure, reject with that reason and stop claiming new indices. A failure not yet observed can race with another worker claiming an index.
  • Already-started operations are not canceled and may still have side effects. Observe their rejections too. No partial result array, retry policy or cancellation API is required.

Check your implementation

Edit tasks/map-limit/starter.mjs:

CANDIDATE=starter node --test tasks/map-limit/check.mjs

For ["a", "b", "c", "d"] with limit 2, a callback that eventually uppercases each item must return ["A", "B", "C", "D"]. The check deliberately releases index 1 before index 0. Index 2 should start immediately after that release, with no more than two operations active. A separate failure check confirms that running work can finish after the returned Promise rejects.

Starter: tasks/map-limit/starter.mjs

export async function mapLimit(items, limit, asyncFn) {
  throw new Error('Implement the ordered bounded worker pool')
}

Take a moment to think about this before revealing the answer

Explain your reasoning or try an implementation before comparing answers.

Explore the full Async/Await material

Premium includes the complete lessons and implementation references. Free practice questions remain available without a subscription.

All course tracks & premium content
From basics to advanced masterclasses
Built for JS/TS developers like you
Real-world tips & common pitfalls
Upgrade to Premium