Detailed explanation
Async/await is JavaScript syntax for working with promises in a readable, sequential style. Declaring a function async makes every call return a promise: a returned value fulfils that promise, while an uncaught exception rejects it. If the function returns another promise, the outer promise adopts its eventual outcome. This matters because asynchronous operations, such as network requests, can be expressed without deeply nested callbacks or long chains of then handlers. However, async does not move the function onto a background thread or automatically make CPU-heavy work non-blocking.
An async function starts executing synchronously and continues until it reaches an await. Await accepts promises, thenables, and ordinary values. Conceptually, JavaScript resolves the awaited value as a promise, suspends the current function, and lets its caller continue. When that promise settles, execution after await resumes through a promise reaction scheduled as a microtask. Fulfilment supplies the resulting value; rejection behaves like a thrown exception at the await expression. Even awaiting an already-fulfilled promise or a plain value defers the continuation. The main thread remains available while waiting, but synchronous work before or after await can still block it.
In the load example, fetch starts the request, and the first await obtains a Response. The second await waits for res.json() to read and parse the response body. Returning await here is meaningful: it lets the surrounding try/catch intercept a rejection from body reading or JSON parsing. Simply returning res.json() would let that later rejection bypass this local catch. Also, fetch generally fulfils for HTTP errors such as 404 or 500, so check res.ok and throw when appropriate. Because the catch only logs, load fulfils with undefined after a caught error; rethrow if callers need to detect failure.
Interviewers also check whether you understand concurrency and error propagation. Consecutive awaits can unnecessarily serialize independent requests; start them together and use Promise.all when all results are required. Keep dependent steps sequential, handle failures at the appropriate boundary, and remember that callers must await or otherwise handle the returned promise. Async/await changes how promise-based code is written, not the underlying asynchronous execution model.
Key points to remember
- Every async function call returns a promise, including when the function returns a plain value.
- Await suspends the current async function, not the main thread; continuation runs as a microtask.
- A rejected awaited promise throws at the await expression and can be caught with try/catch.
- Use Promise.all for independent operations that should run concurrently.
- Check res.ok for HTTP failures, and rethrow caught errors when callers must observe failure.
Common mistakes candidates make
- Assuming async makes CPU-intensive loops run in the background.
- Awaiting independent operations one by one and unintentionally making them sequential.
- Logging an error in catch without realising that this converts the failure into a fulfilled promise unless you rethrow.
Likely follow-up questions
What is the difference between return promise and return await promise?
Both make the async function's returned promise adopt the operation's outcome. Inside a try/catch, return await allows that catch to handle the operation's rejection, whereas returning the promise directly does not.
How can you run two independent requests concurrently?
Use const [a, b] = await Promise.all([fetch('/api/a'), fetch('/api/b')]); both requests start before waiting for their results. Promise.all rejects if either input rejects, but it does not automatically cancel the other request.
Can await be used outside an async function?
Yes, top-level await is supported in JavaScript modules in compatible environments, but not in ordinary non-module scripts. Otherwise, await must appear inside an async function.
Interview tip: Explain the promise contract first, trace execution across one await, and then discuss error propagation and sequential versus concurrent operations.
