Skip to content
Lesson 6 of 15

Step 1 of 5 · Reading · ~3 min

Learn

Async JavaScript

Promise Patterns

You now have promises and async/await. This lesson collects the shapes that real code repeats: wrapping old callback APIs, timeouts, doing things one by one versus all together, collecting failures, and limiting how many things run at once. By the end you can pick the right one for a job and avoid the usual traps.

Wrap a callback API (promisify)

Older Node functions take a callback (err, result) as their last argument. Wrap one in a promise by calling reject for the error and resolve for the result:

javascript

Node's util.promisify(legacyHalf) builds exactly this wrapper for any function that follows the (err, result) convention.

A timeout with Promise.race

Promise.race settles like whichever input settles first, so racing a slow operation against a timer that rejects gives a timeout:

javascript

The loser is not cancelled. The slow operation keeps running and its result is thrown away, and the timer stays pending. A promise cannot be cancelled; you can only stop waiting for it.

One at a time, or all together

Use for...of with await when each step needs the previous one, or when order and load matter. Start everything first and wait once when the pieces are independent:

javascript

Promise.all fails fast: the first rejection rejects the whole thing and you lose the other results. When you want every outcome, use Promise.allSettled, which reports a status for each, and then split them:

javascript

Limiting concurrency

Promise.all(thousandItems.map(fetchOne)) starts a thousand operations at once, which can exhaust sockets, memory or a server's patience. You want at most N running together, and as soon as one finishes the next should begin. The tempting version works in batches:

javascript

It respects the limit but wastes time. Each batch waits for its slowest member, so with a batch of four where one job takes ten seconds, three slots sit idle for nine of them. A better design keeps every slot busy: finishing one job immediately frees a slot for the next one. Building that is this lesson's exercise.

Common mistakes

  • Calling reject or resolve and then continuing to run code in the executor. Add return.
  • Reaching for Promise.all when you need every result, then losing them all to one failure.
  • Thinking a timeout cancels the work. It only stops you waiting.
  • Awaiting inside a plain loop when the calls are independent, which serialises them.
  • Collecting results in the order jobs finish instead of the order they were given.

Your exercise

Write mapLimit(items, limit, fn): at most limit calls in flight, results in input order, no idle slots. The starter times the run on a virtual clock so you can see the difference.

Up nextthis and MethodsObjects in Depth

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…