Asynchronous JavaScript
Async programming is tested in every mid-to-senior JavaScript interview. Companies use it to assess whether you understand how JS handles concurrency despite being single-threaded.
The Event Loop
Call Stack → Web APIs → Callback Queue → Event Loop → Call Stack
- JS runs code on the Call Stack (single-threaded)
- Async operations (setTimeout, fetch) are handed to Web APIs
- When complete, callbacks go to the Callback Queue (macrotask) or Microtask Queue (Promises)
- Event Loop moves callbacks to Call Stack when it's empty
- Microtasks (Promises) always run before macrotasks (setTimeout)
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// Output: 1, 4, 3, 2
Callbacks → Promises → Async/Await
// Callback hell
getUser(id, (user) => {
getPosts(user.id, (posts) => {
getComments(posts[0].id, (comments) => {
// deeply nested...
});
});
});
// Promise chain
getUser(id)
.then(user => getPosts(user.id))
.then(posts => getComments(posts[0].id))
.then(comments => console.log(comments))
.catch(err => console.error(err));
// Async/Await (cleanest)
async function loadData(id) {
try {
const user = await getUser(id);
const posts = await getPosts(user.id);
const comments = await getComments(posts[0].id);
return comments;
} catch (err) {
console.error(err);
}
}