skip to content

Promises and Async/Await

Everything about writing asynchronous JavaScript that reads like sequential code: promise semantics, combinators, async/await, and async iteration. Interviewers lean on this area because the everyday bugs in a JS codebase — dropped errors, accidental serialization, unhandled rejections — all live here.

part ofJavaScriptoverview, primer and where to startread it →
on this pageshow

explore

questions

71 · 5 sections

In a JavaScript promise chain, what happens when the callback you passed to .then() throws an error synchronously — where does execution go next?

level: juniorimportance: must knowfreq 78%
basics
~20 s

A throw inside a .then callback rejects the promise that .then returned, so the chain skips every later fulfillment handler until it reaches a rejection handler — a .catch or a .then second argument — which receives the thrown value.

open as a page

What are the three states of a JavaScript promise, and what happens if the executor passed to new Promise() calls resolve() and then calls reject() a moment later?

level: juniorimportance: must knowfreq 78%
basics
~20 s

A promise is pending, fulfilled, or rejected. The first resolve() or reject() settles it permanently; every later call is silently ignored. So a promise that fulfilled can never become rejected, and its value can never change.

open as a page

In JavaScript, what does calling .then() on a promise return, and how does the value your callback returns affect the next link in the chain?

level: juniorimportance: must knowfreq 78%
basics
~20 s

Every .then() call returns a brand-new promise. Returning a plain value from the callback fulfils that new promise with the value; returning a promise or thenable makes the new promise adopt that one's eventual outcome instead of nesting it.

open as a page

When a .catch() handler on a promise chain returns a value normally instead of rethrowing, what state is the promise it returns in, and how does the rest of the chain behave?

level: middleimportance: must knowfreq 62%
basics
~20 s

A .catch handler that returns normally fulfills the promise .catch returned, with that return value. The chain is back on the success path: the next .then runs with whatever the handler returned, which is undefined if it returned nothing.

open as a page

Given `getUser().then(user => { saveUser(user); }).then(() => console.log('done'))`, where `saveUser` returns a promise, why does "done" print before the save finishes, and what is the fix?

level: middleimportance: must knowfreq 66%
basics
~20 s

The callback never returns the promise from saveUser, so it returns undefined and the chain does not wait for the save. Fix it by returning the promise: user => saveUser(user), or add an explicit return inside the braces.

open as a page

What is the difference between Promise.all and Promise.allSettled in JavaScript, and what does each of them fulfil with?

level: juniorimportance: must knowfreq 78%
basics
~20 s

Promise.all rejects the moment any input promise rejects, and otherwise fulfils with an array of the values in input order. Promise.allSettled waits for every input and always fulfils, giving one {status, value or reason} object per entry.

open as a page

You run `await Promise.all(urls.map(url => fetch(url)))` over an array of 5,000 URLs. What actually happens, and why can Promise.all not limit how many requests are in flight?

level: juniorimportance: must knowfreq 62%
basics
~20 s

All 5,000 requests start immediately: map calls fetch for every URL before Promise.all ever runs. Promise.all only observes promises that are already in flight, so any concurrency cap must be applied where the work is started.

open as a page

In JavaScript, what does the promise returned by Promise.race(iterable) settle with, and what happens if the input that settles first is a rejection?

level: juniorimportance: must knowfreq 68%
basics
~20 s

Promise.race settles with the first input promise to settle and copies its outcome either way. If that first settled input rejects, the race rejects with the same reason, even when other inputs would have fulfilled.

open as a page

Implement `mapWithConcurrency(items, limit, worker)`, which keeps at most `limit` calls to the async `worker` running at once and resolves to an array of results in the original `items` order. How does your implementation work?

level: middleimportance: must knowfreq 55%
basics
~20 s

Start exactly limit runner loops. Each pulls the next index from a shared cursor, awaits worker for that item, and writes the value into results[index] before pulling again. Awaiting all runners gives results in input order.

open as a page

Three uploads are running under a single Promise.all and the second one rejects after 100 ms, so the await throws right away. What is happening to the other two uploads, and what problems does that create?

level: seniorimportance: must knowfreq 55%
basics
~20 s

They keep running to completion. Promise.all's fail-fast rejection only settles the aggregate promise; it has no power to stop work already in flight, so the other uploads still finish, still write their side effects, and their outcomes are silently discarded.

open as a page

In JavaScript, what does calling an `async` function return, and how do a plain `return 42`, a `throw`, and a `return somePromise` inside its body each show up in that returned value?

level: juniorimportance: must knowfreq 85%
basics
~20 s

An async function always returns a promise, never a raw value: return 42 fulfils that promise with 42, throw rejects it with the thrown error, and returning a promise makes the outer promise adopt that promise's eventual outcome.

open as a page

In a JavaScript async function, how does wrapping an `await` in try/catch let you handle a rejected promise, and what exactly does `await` do when the promise it is waiting on rejects?

level: juniorimportance: must knowfreq 78%
basics
~20 s

await rethrows a rejected promise's reason as an ordinary exception at the await point, so a normal try/catch around that await catches it. The catch parameter is bound to whatever value the promise rejected with, which need not be an Error.

open as a page

In JavaScript, a function loops over 10 user IDs and does `const user = await fetchUser(id)` inside the loop body, where each call takes about 200 ms. How long does the loop take overall, why, and how would you make the ten requests run concurrently?

level: juniorimportance: must knowfreq 80%
basics
~20 s

Awaiting inside the loop serializes the requests, so ten 200 ms calls take about 2 seconds. Start all ten promises first and await them together with Promise.all, and the whole batch takes roughly 200 ms.

open as a page

When execution inside an `async` function reaches its first `await`, what does the caller of that function receive at that moment, and is anything blocked while the awaited value is still pending?

level: middleimportance: must knowfreq 75%
basics
~20 s

The caller receives a pending promise straight away. The async function's frame is suspended, not blocked: its locals are parked, control returns to the caller, and the rest of the program keeps running until the awaited value settles and the function resumes.

open as a page

Rewrite this async function as an equivalent promise chain and say what the rewrite reveals about `await`: `async function load(id) { const user = await fetchUser(id); const posts = await fetchPosts(user.id); return { user, posts }; }`

level: middleimportance: must knowfreq 70%
basics
~10 s

Each await splits the function in two: the awaited expression becomes the promise you attach to, and everything after it becomes the callback. So load becomes fetchUser(id).then(user => fetchPosts(user.id).then(posts => ({ user, posts }))).

open as a page

In JavaScript, what does declaring a function as `async function*` let you do that a plain `function*` cannot, and what do you get back when you call `.next()` on the object it returns?

level: juniorimportance: should knowfreq 38%
basics
~20 s

async function* combines await and yield in one body: the function can wait for asynchronous work and then emit a value, repeatedly. Calling next() on the object it returns gives a Promise that fulfils with {value, done}, not that record directly.

open as a page

In JavaScript, what does a `for await (const value of source)` loop do that a plain `for...of` loop does not, and what kinds of source can it consume?

level: juniorimportance: should knowfreq 58%
basics
~20 s

for await...of awaits every step of the iteration. It consumes async iterables (objects with a Symbol.asyncIterator method) and also ordinary iterables, awaiting each produced value before the body runs, so the body sees settled values instead of promises.

open as a page

An async generator throws — or awaits a promise that rejects — after having already yielded two values. Where does that error surface for the consumer, and can the generator still produce values afterwards?

level: middleimportance: should knowfreq 32%
basics
~20 s

The error rejects the promise returned by the next() call that resumed the body, so a for await...of loop throws at that iteration. The generator then moves to the completed state permanently: later next() calls fulfil with {value: undefined, done: true} rather than rethrowing.

open as a page

A function loops through every page of a paginated HTTP API, pushing each page's items into one array, and returns that array once the last page arrives. How would you restructure it as an async generator, and what does that change for the caller?

level: middleimportance: should knowfreq 48%
basics
~20 s

Declare it async function*, await each page inside the cursor loop, and yield the items instead of pushing them into an array. The caller then sees the first item before the last page is fetched and never holds the whole result set in memory.

open as a page

In JavaScript, what must an object provide for `for await...of` to accept it, and what happens if the object defines only `Symbol.iterator`?

level: middleimportance: should knowfreq 44%
basics
~20 s

It needs a Symbol.asyncIterator method returning an object with a next() that produces a promise of { value, done }. If only Symbol.iterator exists, the engine wraps that sync iterator and awaits each value; if neither exists, the loop throws a TypeError.

open as a page

In JavaScript, how do AbortController and AbortSignal work together to cancel an in-flight asynchronous operation?

level: juniorimportance: must knowfreq 62%
basics
~20 s

AbortController holds the trigger and exposes a read-only signal you hand to an operation. Calling controller.abort(reason) sets signal.aborted to true, stores signal.reason, and synchronously fires the signal's abort event, so any operation watching that signal can stop.

open as a page

In Node, fs.readFile(path, encoding, callback) reports its result through an error-first callback (err, data). How would you wrap that call so it returns a Promise instead, and what rules keep such a wrapper correct?

level: juniorimportance: must knowfreq 70%
basics
~10 s

Call the original inside new Promise and translate its callback: reject(err) when the first argument is truthy, otherwise resolve(data). The executor runs immediately, must settle exactly once, and should contain nothing but that translation.

open as a page

A JavaScript helper adds a timeout by racing an operation against a rejecting timer: `const timeout = (ms) => new Promise((_, reject) => setTimeout(() => reject(new Error('timed out')), ms)); const data = await Promise.race([slowRequest(), timeout(1000)]);`. If slowRequest() takes 5 seconds, what does the caller observe, and what happens to slowRequest() itself?

level: juniorimportance: must knowfreq 68%
basics
~20 s

The await rejects after one second with the timer's error, but slowRequest() keeps running to completion and its result is simply discarded. Promise.race only reports the first settlement; it cannot stop or unsubscribe from the loser.

open as a page

In JavaScript, what is a "floating promise", and what happens if an async function you called without awaiting it ends up rejecting?

level: juniorimportance: must knowfreq 75%
basics
~20 s

A floating promise is one whose result nobody awaits or attaches a handler to. If it rejects, the error never reaches the caller's try/catch: browsers fire an unhandledrejection event and log it, and Node terminates the process by default.

open as a page

Why does JavaScript have no promise.cancel(), and what does AbortController give you instead?

level: middleimportance: must knowfreq 58%
basics
~20 s

A promise is a read-only handle to a result that only its creator can settle, and any number of consumers may hold it, so no consumer can be allowed to cancel it. JavaScript therefore signals cancellation out-of-band with AbortController.

open as a page