When an interviewer asks you to explain promises and async/await, they want to see that you understand both the what and the why of modern JavaScript concurrency. Below is a compact framework you can use to structure your response, plus the deeper details you should be ready to discuss.
One‑sentence definition
A promise is a JavaScript object that represents the eventual completion (or failure) of an asynchronous operation and yields its result, while async/await are language keywords that let you write promise‑based code that looks like ordinary synchronous statements.
How promises work under the hood
- States – A promise starts as pending. It can transition to fulfilled (with a value) or rejected (with a reason). Once settled, the state never changes.
- Executor – When you create a promise with
new Promise((resolve, reject) => { … }), the executor runs immediately and receives two callbacks. - Chaining –
promise.then(onFulfilled, onRejected)returns a new promise. The returned promise adopts the value returned by the handler, allowing you to build a chain of asynchronous steps. - Microtasks – After the current call stack empties, the JavaScript engine processes the promise reaction queue (a microtask queue). This guarantees that
.thencallbacks run before the next macrotask (e.g.,setTimeout).
async/await mechanics
- The
asynckeyword marks a function as returning a promise automatically. - Inside an
asyncfunction,await exprpauses execution untilexprsettles. The expression must be a promise; non‑promise values are wrapped withPromise.resolve. - While the function is paused, the engine schedules the rest of the code as a microtask, preserving the same ordering guarantees as regular promise chains.
- Errors thrown inside an
asyncfunction are captured and cause the returned promise to reject, which you can handle withtry/catchor a downstream.catch.
Trade‑offs: readability vs. control
| Aspect | Promise chains | async/await |
|---|---|---|
| Readability | Can become nested or hard to follow with many .then calls. | Linear flow resembles synchronous code; easier to read for most developers. |
| Error handling | Errors must be propagated through each .catch or a final .catch. | try/catch works naturally, but you must remember that await only catches the awaited promise. |
| Parallelism | Promise.all is explicit; you can start many promises before awaiting. | You can still use Promise.all, but a naïve await inside a loop runs sequentially, which may be slower. |
| Debugging | Stack traces can be fragmented across then callbacks. | Modern browsers preserve async stack traces, making debugging simpler. |
| Compatibility | Works in any environment that supports ES6 promises. | Requires ES2017 or a transpiler for older browsers. |
When to prefer one over the other
- Use async/await for straightforward, linear flows where readability is a priority.
- Use promise methods (
Promise.all,Promise.race, etc.) when you need to coordinate multiple concurrent operations or when you want to avoid the overhead of an extra async function.
Concrete example
// Fetch user data and their posts, then log a summary.
async function showUserSummary(userId) {
try {
// Start both requests in parallel.
const [user, posts] = await Promise.all([
fetch(`/api/users/${userId}`).then(r => r.json()),
fetch(`/api/users/${userId}/posts`).then(r => r.json())
]);
console.log(`${user.name} has ${posts.length} posts.`);
} catch (e) {
console.error('Failed to load data:', e);
}
}
If you wrote the same logic with pure promises, you would need two separate fetch calls, two .then chains, and a final Promise.all to combine them. The async version is shorter and mirrors the mental model of “get both things, then do something.”
Typical interview follow‑up questions
- Why does
awaitonly pause the current async function and not the whole call stack? – Becauseawaityields control back to the event loop, allowing other tasks to run while the promise settles. - What happens if you forget to
awaita promise inside an async function? – The promise starts executing, but the function continues immediately; any errors become unhandled rejections unless you later attach a.catch. - How do you run multiple async operations in parallel with
await? – By creating the promises first (e.g., viafetch) and then awaitingPromise.allon the array of promises. - Can you
awaita non‑promise value? – Yes; the value is automatically wrapped withPromise.resolve, so the await resolves immediately. - What is the difference between
Promise.resolve()andnew Promise(resolve => resolve(value))? – Both produce a fulfilled promise, butPromise.resolvemay return an existing promise unchanged, avoiding extra allocation.
60‑second spoken answer (sample)
"A promise is an object that stands for a value that will be available later, either as a result or an error. It starts pending, then settles to fulfilled or rejected, and you attach handlers with
.thenor.catch.async/awaitare just syntax sugar: marking a functionasyncmakes it return a promise, andawaitpauses that function until the awaited promise settles, letting you write asynchronous code that reads like normal sequential code. The main trade‑off is readability—awaitmakes most flows easier to follow—but you have to be careful with parallelism; a naïveawaitinside a loop runs sequentially, so you often combineawaitwithPromise.allto keep things concurrent. Error handling is also simpler withtry/catchinside an async function. In practice, I useasync/awaitfor most business logic and fall back to raw promises when I need fine‑grained control over concurrency."
How to practice this
- Write the same logic twice – Implement a small feature (e.g., fetching data and combining results) once with pure promises and once with async/await. Compare length, readability, and error handling.
- Record yourself – Use Call Assistant to capture a 60‑second spoken answer, then replay it to check pacing and whether you stay within the time limit.
- Play the interview game – Pair with a colleague and take turns asking each other “Why would you use
Promise.allhere?” and “What happens if you forget anawait?” Keep the conversation focused on the concepts, not on any specific company.
FAQ
- What is the difference between a resolved promise and a fulfilled promise? A resolved promise is a generic term meaning the promise is no longer pending; it may be fulfilled with a value or rejected with a reason. "Fulfilled" specifically refers to the successful case.
- Can I use
awaitat the top level of a script? Modern ECMAScript modules allow top‑levelawait, but traditional scripts still require an async function wrapper. - Why do promise callbacks run before
setTimeoutcallbacks? Promise reactions are placed in the microtask queue, which the engine processes after the current call stack but before the next macrotask queue (wheresetTimeoutcallbacks live). - Is it safe to mix
async/awaitwith.thenchains? Yes; they interoperate because both ultimately produce promises. Just be consistent about error handling to avoid duplicated catches.
Frequently asked questions
What is a promise in JavaScript?
A promise is an object that represents a value that may become available later. It starts pending and can settle to either fulfilled (with a value) or rejected (with an error).
How does async/await simplify promise usage?
`async` marks a function to always return a promise, and `await` pauses that function until the awaited promise resolves, letting you write asynchronous code that looks like sequential statements.
When should I prefer raw promises over async/await?
Prefer raw promises when you need fine‑grained control over concurrency, such as building custom combinators or when you must start many operations without awaiting each immediately.
What common mistake leads to unhandled promise rejections?
Forgetting to `await` a promise inside an async function or not attaching a `.catch` to a promise chain can cause the rejection to go unhandled.
#concept#promises#async/await#javascript#interview#promises and async/await