EXERCISE
1JavaScript uses a concurrency model based on the event loop. It is not truly parallel, but it feels concurrent because it switches between tasks very quickly.
JavaScript's concurrency model is often summarised as "single-threaded and asynchronous", which sounds contradictory until you separate two things: what executes your code, and what does the waiting.
Only one thing executes your code, and that is the call stack. It handles one frame at a time and cannot be interrupted. Nothing runs in parallel with your functions.
The waiting is done by the environment, which is not limited to one thread at all. A browser can run several network requests, a handful of timers and a file read simultaneously, because those are handled by the browser's own machinery.
console.log("start");
setTimeout(() => console.log("timer 1"), 1000);
setTimeout(() => console.log("timer 2"), 1000);
setTimeout(() => console.log("timer 3"), 1000);
console.log("end");
Three timers counting the same second, concurrently, outside JavaScript. When they expire, their callbacks queue and run one at a time on the single thread. The waiting overlapped; the executing did not.
This is why parallel requests are faster than sequential ones even with one thread:
const urls = ["/a", "/b", "/c"];
urls.forEach(function (url) {
fetch(url).then(function (res) {
console.log(url, res.status);
});
});
All three requests are in flight at once. The network waiting overlaps completely. Only the handling of each response is serialised, and that part is fast.
What you cannot overlap is computation:
function heavy() {
let total = 0;
for (let i = 0; i < 500000000; i++) total += i;
return total;
}
setTimeout(heavy, 0);
setTimeout(heavy, 0);
Two timers, but no at all. Each heavy occupies the thread for its full duration, and the second waits for the first. Scheduling computation does not make it parallel; it only changes when the freeze starts.
The dividing line is the same one from earlier in this unit. Work that waits on something external can overlap, because the waiting is not done by JavaScript. Work that computes cannot, because computing is exactly what the single thread does.
applies the same model with a different cast: libuv and a small thread pool instead of Web APIs, the same single thread, and the same event loop moving finished callbacks back onto it.