Master the fundamentals of async programming in JavaScript - Essential knowledge for building responsive web applications.
- π― Overview
- β‘ JavaScript's Fundamental Nature
- π Achieving Asynchronous Behavior
- β° Timeouts and Intervals
- π Callbacks
- π€ Promises
- β³ Async/Await
- π The Event Loop
- π Additional Resources
Asynchronous JavaScript enables non-blocking operations, allowing applications to remain responsive while performing time-consuming tasks like network requests, file operations, or database queries.
- Non-blocking: UI stays responsive during async operations
- Concurrent execution: Multiple operations can run simultaneously
- Better user experience: No frozen interfaces during data loading
JavaScript is inherently synchronous, blocking, and single-threaded:
- Synchronous: Code executes line by line, one at a time
- Blocking: Long operations freeze the entire application
- Single-threaded: Only one operation can run at any moment
// This blocks everything for 5 seconds
function fetchData() {
const start = Date.now();
while (Date.now() - start < 5000) {
// Busy waiting - blocks the main thread
}
return "Data";
}When synchronous operations block the main thread:
- β° Timers are delayed
- π±οΈ User interactions are ignored
- π Event Loop cannot process queued tasks
- β Application becomes unresponsive
JavaScript relies on Web APIs (browser) or Node.js APIs to achieve asynchronicity.
setTimeout/setInterval- Timer-based operationsfetch- Network requests- DOM Events - User interactions
- File API - File operations
Synchronous Code β Web API β Background Processing β Callback Queue β Event Loop β Call Stack
Executes a function once after a specified delay.
Event Loop Behavior:
setTimeoutis pushed to Call Stack- Timer is handed to Web API (background)
- Call Stack continues with other code
- When timer expires, callback goes to Task Queue
- Event Loop moves callback to Call Stack when empty
Repeatedly executes a function at specified intervals.
Important Notes:
β οΈ Minimum delay, not guaranteed - Execution may be delayed if Call Stack is busy- β Recursive setTimeout preferred - Guarantees timing between executions
- β setInterval can overlap - If execution takes longer than interval
console.log("1οΈβ£ Start");
setTimeout(() => console.log("β° Timer"), 0);
console.log("2οΈβ£ End");
// Output: 1οΈβ£ Start β 2οΈβ£ End β β° Timer
// Event Loop: Call Stack β Task Queue β Call StackFunctions passed as arguments to other functions, executed when a specific event occurs.
- Execute immediately when called
- Examples: Array methods (
map,filter,reduce) - No Event Loop involvement
- Execute after a delay or event
- Examples:
setTimeout, event handlers, API calls - Managed by Event Loop
fetchUser(userId, (user) => {
fetchPosts(user.id, (posts) => {
fetchComments(posts[0].id, (comments) => {
// Nested callbacks become hard to read
});
});
});- Each async callback goes to appropriate queue
- Callback Hell creates complex queue management
- Error handling becomes difficult across nested levels
A cleaner way to handle asynchronous operations with better error handling and chaining.
- Pending - Initial state, neither fulfilled nor rejected
- Fulfilled - Operation completed successfully
- Rejected - Operation failed
- Chainable -
.then()and.catch()return new promises - Error handling - Centralized error management
- Static methods -
Promise.all(),Promise.race(),Promise.allSettled()
console.log("1οΈβ£ Start");
Promise.resolve().then(() => console.log("π€ Promise"));
setTimeout(() => console.log("β° Timer"), 0);
console.log("2οΈβ£ End");
// Output: 1οΈβ£ Start β 2οΈβ£ End β π€ Promise β β° Timer
// Event Loop Priority: Microtask Queue > Task Queue- Promises go to Microtask Queue (higher priority)
- setTimeout goes to Task Queue (lower priority)
- Event Loop processes Microtasks before Tasks
Syntactic sugar over promises, making asynchronous code look synchronous.
- async functions always return promises
- await pauses execution until promise settles
- try/catch provides clean error handling
- Sequential vs Concurrent execution patterns
async function sequential() {
const result1 = await apiCall1(); // Waits 2s
const result2 = await apiCall2(); // Waits 1s
// Total time: 3 seconds
}async function concurrent() {
const [result1, result2] = await Promise.all([
apiCall1(), // 2s
apiCall2() // 1s
]);
// Total time: 2 seconds (longest request)
}- await creates microtasks
- Async functions don't block the main thread
- Event Loop continues processing other tasks during await
The heart of JavaScript's asynchronous behavior, managing the single-threaded execution model.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β JavaScript Runtime Environment β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β Memory Heap β β Call Stack β β Web APIs β β
β β β β β β β β
β β β’ Variables β β β’ Functions β β β’ setTimeout β β
β β β’ Objects β β β’ Execution β β β’ setInterval β β
β β β’ Functions β β β’ LIFO Order β β β’ fetch β β
β β β’ Closures β β β’ One at a time β β β’ DOM Events β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β Microtask Queue β β Task Queue β β Event Loop β β
β β β β β β β β
β β β’ Promise .then β β β’ setTimeout β β β’ Orchestrator β β
β β β’ Promise .catchβ β β’ setInterval β β β’ Priority: β β
β β β’ async/await β β β’ DOM Events β β Microtask > β β
β β β’ queueMicrotaskβ β β’ User Events β β Task Queue β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
while (true) {
if (callStack.isEmpty()) {
if (microtaskQueue.hasItems()) {
// Process all microtasks (higher priority)
const task = microtaskQueue.dequeue();
callStack.push(task);
} else if (taskQueue.hasItems()) {
// Process one task (lower priority)
const task = taskQueue.dequeue();
callStack.push(task);
}
}
}
- Synchronous Code (Call Stack)
- Microtask Queue (Promises, async/await)
- Task Queue (setTimeout, setInterval, events)
javascript
// Interactive demonstration
console.log("π¬ Event Loop Demo Starting...");
// Phase 1: Synchronous execution
console.log("π Phase 1: Synchronous code starts");
// Phase 2: Async operations scheduled
setTimeout(() => {
console.log("β° Phase 3: Task Queue (setTimeout)");
}, 0);
Promise.resolve().then(() => {
console.log("π€ Phase 2: Microtask Queue (Promise)");
});
console.log("π Phase 1: Synchronous code ends");
// Expected execution order:
// π Phase 1: Synchronous code starts
// π Phase 1: Synchronous code ends
// π€ Phase 2: Microtask Queue (Promise)
// β° Phase 3: Task Queue (setTimeout)
- Single-threaded: JavaScript can only do one thing at a time
- Non-blocking: Async operations don't freeze the application
- Event-driven: Code responds to events and timers
- Queue-based: Tasks wait in queues until Call Stack is empty
- Priority system: Microtasks have higher priority than tasks