Asynchronous programming is central to modern JavaScript and TypeScript development. Interviewers frequently probe candidate knowledge of the event loop, promise resolution order, error handling, and concurrency primitives. Here are essential async questions with runnable code examples.
1. Trace the Output: Call Stack, Promises, and SetTimeout
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
queueMicrotask(() => console.log("4"));
console.log("5");
// Output: 1, 5, 3, 4, 2Explanation: Synchronous code (1, 5) runs first. Next, the microtask queue drains completely (3, 4). Finally, the macrotask callback (2) executes.
2. Implement Promise.all from Scratch
function myPromiseAll(promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument must be an array"));
}
const results = [];
let completedCount = 0;
if (promises.length === 0) {
return resolve([]);
}
promises.forEach((item, index) => {
Promise.resolve(item)
.then((val) => {
results[index] = val;
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
})
.catch(reject); // Fail fast on first rejection
});
});
}*Practice Implement Promise.all live on Pairlet.*
3. Compare Promise Combinators
- •
Promise.all([p1, p2]): Resolves when ALL resolve, rejects if ANY rejects. - •
Promise.allSettled([p1, p2]): Resolves when ALL settle (fulfilled or rejected). - •
Promise.race([p1, p2]): Settles as soon as the FIRST promise settles. - •
Promise.any([p1, p2]): Resolves as soon as the FIRST promise fulfills; rejects if ALL reject.
4. Sequential vs. Parallel Execution with async/await
// Sequential (takes ~2 seconds total)
async function fetchSequential() {
const res1 = await fetchItem(1); // 1s
const res2 = await fetchItem(2); // 1s
return [res1, res2];
}
// Parallel (takes ~1 second total)
async function fetchParallel() {
const p1 = fetchItem(1);
const p2 = fetchItem(2);
return await Promise.all([p1, p2]);
}5. Implement a Concurrency Limiter (Task Pool)
Limit the number of active concurrent promise executions to maxConcurrency.
async function mapConcurrent(items, limit, asyncFn) {
const results = [];
const executing = new Set();
for (const [index, item] of items.entries()) {
const p = Promise.resolve().then(() => asyncFn(item, index));
results[index] = p;
executing.add(p);
const clean = () => executing.delete(p);
p.then(clean, clean);
if (executing.size >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}*Try the Concurrency Limiter live problem on Pairlet.*
6. How to Cancel an In-Flight Async Request?
Use the browser-native AbortController:
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
.then(res => res.json())
.catch(err => {
if (err.name === 'AbortError') {
console.log('Request was cancelled');
}
});
// Cancel the request
controller.abort();7. Retrying Failed Promises with Exponential Backoff
async function fetchWithRetry(fn, retries = 3, delayMs = 500) {
try {
return await fn();
} catch (err) {
if (retries <= 0) throw err;
await new Promise(r => setTimeout(r, delayMs));
return fetchWithRetry(fn, retries - 1, delayMs * 2);
}
}Practice Async Live Coding
Frequently Asked Questions
What is the main difference between Promise.all and Promise.allSettled?
Promise.all rejects immediately if any input promise rejects (fail-fast behavior). Promise.allSettled waits for all input promises to either resolve or reject, returning an array of outcome objects with status and value/reason.
Conduct Live Coding Interviews in the Browser
No candidate sign-up required. Create a room, share the link, and code together in real time with shared code execution.