When to Use Async JavaScript?
I had this question asked recently: "How do I know which parts of JavaScript are async and which ones aren't?"
You'll find plenty of tutorials that tell you setTimeout is async, fetch is async, and so on. But they
usually don't explain the underlying rule that determines
what can be async in the first place.
For example, why can I do this:
console.log("This runs first")
setTimeout(() => {
console.log("This runs later (after 200ms)")
}, 200)
console.log("This also runs immediately - setTimeout doesn't block!") But not this:
// This BLOCKS everything - even with async/await!
async function expensiveCalculation() {
let sum = 0
for (let i = 0; i < 1000000000; i++) {
sum += i
}
return sum
}
const result = await expensiveCalculation() // Still freezes the page! Here's the key to understanding async Javascript: JavaScript itself is not asynchronous. But operations that happen outside of JavaScript are.
The setTimeout Example
At first glance, setTimeout seems like a core
JavaScript feature. But here's the twist: the actual timing
mechanism lives outside JavaScript entirely.
When you call setTimeout, here's what
really happens:
- JavaScript delegates the timing work to the browser runtime. JavaScript itself doesn't do any timing
- The browser (not JavaScript) handles the actual timer using its own optimized C++ code
- When the timer completes, the browser queues a callback event
- JavaScript picks up the event and executes your callback
If you're using Chrome, Chrome is handling the timer. If you're using Firefox, Firefox is handling it. The browser itself is doing the work, not JavaScript.
All the actual work happened outside JavaScript's engine. JavaScript just delegated the problem to the browser and waited for the result. That's why it can be asynchronous, because the heavy lifting happens elsewhere.
The Calculation Example
In contrast, that expensive loop runs inside JavaScript's engine. There's nowhere to offload it. It just blocks the main thread, freezing your entire page.
Even if you mark the function as async and use
await, it won't help. The work still
happens inside JavaScript's engine, blocking the main
thread. The work has to happen outside JavaScript for it
to be truly asynchronous.
Other Async Operations
This also applies to async operations like:
- HTTP requests
setInterval- Intersection Observer
These all happen outside JavaScript's engine, so they can be asynchronous.
What About localStorage? It's Outside JavaScript Too
Good question! localStorage is a browser feature
that happens outside JavaScript's engine. Exactly like setTimeout
and fetch. So shouldn't it be async?
Actually, localStorage is synchronous. Even though
it happens outside JavaScript, reading and writing to localStorage is performant and typically very fast (it has a 5MB limit
and uses the browser's storage system).
However, if you add too much data to localStorage, you might run into performance issues. Since it's
synchronous, large write operations can block the main
thread, potentially freezing your page. The browser
designers decided the trade-offs weren't worth making it
async for typical use cases, but be mindful of the
amount of data you're storing.