Asynchronous JavaScript Explained
About 500 wordsAbout 6 min
2025-08-05
This page details the mechanisms JavaScript uses to handle non-blocking operations, from the event loop to modern async/await
.
Callbacks
The original pattern for async operations. Prone to "Callback Hell" (deeply nested callbacks) which is hard to read and maintain.
The Past
Promises
An object representing the future result of an async operation. It formalizes the process with .then()
for success and .catch()
for errors, enabling clean chaining.
The Present
async/await
Syntactic sugar over Promises that allows writing async code that looks synchronous, dramatically improving readability and error handling with try...catch
.
The Future
Understanding the Event Loop JavaScript's concurrency model is based on the event loop.
- Execution: Code is run on the Call Stack.
- Offloading: When an async operation (e.g.,
fetch
,setTimeout
) is called, it's handed off to the browser/Node.js API to run in the background. The Call Stack is now free. - Queuing: Once the background API finishes, it places its callback function into a queue. Promises place their callbacks in the Microtask Queue, while others use the Callback (or Macrotask) Queue.
- Looping: The Event Loop constantly checks if the Call Stack is empty.
- Prioritizing: If the stack is empty, the Event Loop first processes all tasks in the Microtask Queue before moving to a single task from the Callback Queue. This is why Promises resolve before
setTimeout
callbacks.
Mastering Promises Beyond simple
.then()
and.catch()
, mastering promises involves understanding chaining and combinators.Chaining and Value Passing: Whatever you
return
from a.then()
callback becomes the value passed to the next.then()
in the chain. If you return a new Promise, the chain will wait for that new Promise to resolve.Promise.resolve(10) .then(value => value * 2) // returns 20 .then(value => { console.log(value); // 20 return Promise.resolve(value + 5); // returns a new promise }) .then(value => console.log(value)); // 25
Promise Combinators:
Promise.all(iterable)
: Great for running multiple independent async operations in parallel. It waits for all to succeed; fails if any one fails.Promise.allSettled(iterable)
: Use when you need to know the outcome of every promise, regardless of whether they succeed or fail.Promise.race(iterable)
: Useful for scenarios with a timeout, where you race a data promise against asetTimeout
promise.Promise.any(iterable)
: Good for fetching a resource from multiple endpoints, succeeding as soon as the first one responds.
Advanced
async/await
Patterns Whileawait
makes code look synchronous, it's important to avoid creating unnecessary waterfalls.Sequential vs. Parallel Execution:
// BAD: Sequential waterfall - each await waits for the previous one async function getSequentialData() { const user = await fetchUser(); const posts = await fetchPosts(); // Waits for fetchUser to complete return { user, posts }; } // GOOD: Parallel execution - requests start concurrently async function getParallelData() { const userPromise = fetchUser(); const postsPromise = fetchPosts(); // Promise.all waits for both to complete const [user, posts] = await Promise.all([userPromise, postsPromise]); return { user, posts }; }
Changelog
2aa48
-web-deploy(Auto): Update base URL for web-pages branchon
Copyright
Copyright Ownership:WARREN Y.F. LONG
License under:Attribution-NonCommercial-NoDerivatives 4.0 International (CC-BY-NC-ND-4.0)