Async & Concurrency
Doing other work while waiting — without freezing the program.
Prerequisites:Functions
Story — the chef who orders pizza
A chef has a 30-minute pizza in the oven. While it bakes, they don't stand staring at the door. They chop salad, set the table, answer the phone — and when the timer beeps, they pull the pizza out. The kitchen looks busy because waiting time is reused.
Async is that. When your code says await fetch(url), the runtime puts that work on a list and runs other code while the network does its job. When the result arrives, the original function picks up where it left off. One thread, many things in flight.
Awaita says
await means "pause this function until the result is ready." It does not mean "freeze the program." The rest of the world keeps running.See it — sequential vs concurrent
Two ways to run two async tasks: sequentially (await one, then await the other — total time is the sum) or concurrently (kick both off, then await both — total time is the max). The same code, two shapes, very different latency.
Awaita says
Try it — when does async help?
Exercise. Which of these workloads benefits most from async/await on a single thread?
- (a) A program that does heavy math on a 1 GB array.
- (b) A web server handling 1,000 simultaneous requests, mostly waiting on the database.
- (c) A script that reads a file, transforms it, and writes a new one.
Reveal answer
(b) is the canonical async win. Each request spends 99% of its time waiting on I/O — async lets one thread interleave hundreds. (a) is CPU-bound — async helps nothing; you need parallelism (multiple cores). (c) might benefit slightly if you can read and write at once, but for a small script it doesn't matter.
Code it — five languages, two patterns
Python and JavaScript share async/await almost identically. Java uses CompletableFuture. Go avoids the colour-the-functions problem entirely with goroutines + channels. Different shapes, same idea.
import asyncio
async def fetch_user(id):
await asyncio.sleep(1) # simulate network
return {"id": id, "name": f"User{id}"}
async def main():
# Sequential — total ~2s
a = await fetch_user(1)
b = await fetch_user(2)
# Concurrent — total ~1s
a, b = await asyncio.gather(fetch_user(1), fetch_user(2))
asyncio.run(main())Quiz it — make it stick
No Dumb Questions
Real questions other learners asked on this page.
What's the difference between concurrency and parallelism?
Concurrency = many things in progress (interleaved). Parallelism = many things actually executing simultaneously (multiple cores). Async/await is concurrency on one thread; threads/processes are parallelism.Why does async code "infect" everything?
Because to await a promise, the surrounding function must itself be async (or use a callback). It propagates upward until you reach the top of the program. Some languages call this "function colouring."What is a race condition?
Two operations that interleave in a way the programmer didn't anticipate, producing wrong results. Classic example: two threads incrementing the same counter — they read 5, both add 1, both write 6, and the count is off by one.