Concurrency Is a Timeframe, Not an Instant
Why concurrency means two operations sharing a stretch of time, not the same instant, and why the event loop never lets one of them cut in line.
Here's the actual idea this post teaches, in one sentence: concurrency means two things sharing the same stretch of time, not the same instant, so a single thread can be concurrent even though only one thing on it is ever truly running.
I used to think concurrency was just a fancier word for parallelism. It isn't. Parallelism is about a single instant, two things physically happening at once.
Concurrency measures something else entirely: a stretch of time, not an instant.
Breaking a Task Into Smaller Tasks
Say your app needs to do two things around the same moment: save a note to the server, and update a live word count as the user keeps typing.
Neither of those is really just one single step.
Saving the note breaks into four steps:
- Validate the note's content
- Turn it into JSON
- Send it to the server
- Confirm the response came back clean
Updating the word count breaks into three:
- Read the current text
- Count the words
- Repaint the counter on screen
Call these small steps "micro" and the two operations they belong to "macro." Pretend each micro step takes exactly one second.
Whichever order those seven one-second steps run in, the whole job takes seven seconds, because JavaScript only has one thread. There's no way around that total.
What changes is the order those seven steps get scheduled in, and that order is the entire difference between an app that feels responsive and one that feels frozen.
Frozen vs. Responsive, Same Total Time
The first row finishes all four save steps before touching the word count once. If you tried typing while a save was in progress, the page would sit there frozen, then suddenly catch up the moment the save finished.
The second row still takes seven seconds total, but the typing never stalls for more than a second at a time. That's concurrency: two macro-level operations sharing the same timeframe, even though only one micro-level step is ever actually running.
This really happened. The web platform used to let you make a request that froze everything else on the page until the response came back, a synchronous request.
Browsers have since removed it completely, because forcing every page into the first row above, frozen until one thing fully finishes, made those pages feel broken.
The Event Loop Doesn't Let Anyone Cut in Line
The thing deciding which micro step runs next is called the event loop, and it runs one simple rule: first come, first served.
When your save request finally gets a response back from the server, its callback doesn't get to interrupt whatever is currently running. It joins a queue. If the browser's rendering engine is mid-repaint, or the garbage collector is mid-cleanup, your callback waits its turn like everything else.
This is also why animations sometimes visibly stutter. The rendering engine wants to repaint the screen on a strict schedule.
If something else on the single thread is still running when that repaint's turn comes up, the repaint has to wait. The frame drops.
Why This Justifies the Rest of the Series
The plan for this series is to write the same program with six different tools. Every one of those tools is really just a different way of deciding what goes into that one queue, and in what order.
Here's the part worth sitting with: a plain callback can technically express any of this scheduling. Nothing in this series introduces some new capability a callback couldn't already do.
What a callback can't handle well is a lot of these at once. Two operations taking turns is easy to manage by hand.
A real app juggles a dozen requests at once, not two. Some wait on each other, some race to see which finishes first, some need three specific ones out of five to finish before moving on. Coordinate all of that by hand and it turns into a tangle no one can read a year later.
That tangle, not any missing feature, is the actual reason this series exists. Managing that complexity is what asynchronous programming is for, and that's exactly where the next section picks up.
Keep reading