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 15 essential async questions with runnable code solutions.
1. Trace the Output: Call Stack, Promises, and SetTimeout
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
queueMicrotask(() => console.log("4"));
console.log("5");
// Output: 1, 5, 3, 4, 2
``
Explanation: 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 = [];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:
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 Ready to test async problems live? [Create a free Pairlet interview room](https://www.pairlet.dev/interview/new) to collaborate in real time with candidates.
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 with Zero Friction
No candidate sign-up required. Create an instant room, share the link, and code together in real time with shared code execution.