-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
231: Common JavaScript Interview Questions on Async Code
A few years ago, I interviewed a candidate who had a sparkling portfolio and five years of experience. We got to the technical round, and I gave them a small snippet of code containing a setTimeout, a Promise.resolve(), and a standard console.log. I asked them to predict the order of the logs. They confidently told me the timer would go first because it was defined first in the code. When I explained that the Promise would actually jump the queue, they looked genuinely blindsided. They knew how to use async/await to fetch data, but they didn't understand the "plumbing" of the JavaScript engine. That's the difference between a coder and an engineer, and it's exactly what interviewers are hunting for when they grill you on async code.
The Microtask Queue vs. The Macrotask Queue
When an interviewer asks you "What is the Event Loop?", they aren't just looking for a textbook definition. They want to know if you understand why a Promise behaves differently than a setTimeout. In JS, we have the Call Stack, the Macrotask Queue (where setTimeout and setInterval live), and the Microtask Queue (where Promise callbacks and MutationObserver live).
Here is the crucial bit: the Event Loop will not move on to the next Macrotask until the Microtask Queue is completely empty. If a Promise resolves and adds a callback to the microtask queue, that callback will execute before any pending timers, even if those timers were set to 0 milliseconds. I've seen people lose a lot of points by forgetting that promises basically "cut the line."
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');
// Output: A, D, C, B
// Why? A and D are synchronous. C is a microtask. B is a macrotask.
The async/await Loop Trap
This is perhaps the most common "gotcha" in mid-level interviews. You'll likely be asked why an async function inside a .forEach() doesn't actually wait for the promises to resolve. It's because forEach is just a higher-order function that executes a callback; it doesn't check if that callback returns a promise, and it certainly doesn't await it.
If you need to execute async operations in sequence, you have to use a for...of loop or a standard for loop. If you use forEach, the loop will finish executing almost instantly, firing off all your async calls in parallel without waiting for any of them to finish. It's a subtle bug that can cause absolute chaos in a production database if you're accidentally hammering an API with 1,000 simultaneous requests instead of one at a time.
Parallel Execution with Promise.all
Once you've proven you can handle sequential code, the interviewer will usually pivot to performance. They'll ask: "Your code works, but it's slow. How do you fix it?" This is your cue to talk about Promise.all or Promise.allSettled.
If you have three API calls that don't depend on each other, awaiting them one by one is a waste of time. You're creating a waterfall where each request waits for the previous one to round-trip to the server. By wrapping them in Promise.all([p1, p2, p3]), you trigger all three requests simultaneously. Just remember the catch: Promise.all is "fail-fast." If any single promise rejects, the whole thing rejects immediately. If you need to know the result of every call regardless of failure, Promise.allSettled is your best friend. I always mention this distinction in interviews because it shows you've actually dealt with flaky APIs in the real world.
📋 Practical Task
Refactoring the Sequential API Fetcher
You have been handed a piece of legacy code that fetches user profiles and their associated posts. Currently, the code is written sequentially, meaning it waits for the user profile to load, then waits for the posts to load, and then waits for the settings to load. This is causing a noticeable lag in the UI.
Your Goal: Refactor the fetchUserData function. The userProfile must be fetched first (because the other two calls need the userId from it), but the userPosts and userSettings should be fetched in parallel to save time.
const mockApi = {
getUserProfile: () => new Promise(res => setTimeout(() => res({ id: 1, name: 'Dev' }), 100)),
getUserPosts: (id) => new Promise(res => setTimeout(() => res(['Post 1', 'Post 2']), 100)),
getUserSettings: (id) => new Promise(res => setTimeout(() => res({ theme: 'dark' }), 100)),
};
async function fetchUserData() {
console.time('FetchTime');
// CURRENT SEQUENTIAL IMPLEMENTATION
const profile = await mockApi.getUserProfile();
const posts = await mockApi.getUserPosts(profile.id);
const settings = await mockApi.getUserSettings(profile.id);
console.timeEnd('FetchTime');
return { profile, posts, settings };
}
fetchUserData().then(data => console.log('Data loaded:', data));
Modify the function so that getUserPosts and getUserSettings run concurrently. Your FetchTime should drop from roughly 300ms to roughly 200ms.
There are no comments for now.