Learning paths / Async without the mystery

Async without the mystery

Event loops, await, and what blocks them, in JavaScript, Python, C#, Rust and Kotlin.

Start: step 1

8 steps · progress is saved in this browser

  1. The JavaScript event loop: microtasks before timersJavaScript · BasicsOne thread runs the whole script first. Then every microtask (the promise), and only then the next task (the timeout).
  2. A CPU-heavy loop blocks the JavaScript event loopJavaScript · BasicsA ticker logs every 20 ms. A 200 ms busy loop holds the only JS thread, so no tick runs until it ends.
  3. time.sleep vs asyncio.sleep: blocking the event loopPython · Basicsslow_call uses time.sleep inside a coroutine. It blocks the loop's only thread, so the ticker can't tick until it ends.
  4. Which thread runs the code after await?C# · IntermediateFetchAsync starts on the caller's thread. After await Task.Delay, the rest of it, and of Main, runs on a thread-pool thread.
  5. The .Result deadlock on a UI threadC# · AdvancedA click handler blocks the UI thread in .Result. The rest of LoadAsync needs that same thread, so neither can go on.
  6. Tokio tasks switch only at .awaitRust · IntermediateTwo tasks share one thread. They take turns, and every switch happens at an .await.
  7. Blocking in async code stalls every taskRust · Intermediatemain calls a blocking sleep on the runtime's only thread. The ticker can't tick until it ends.
  8. Kotlin coroutines take turns on one threadKotlin · BasicsTwo coroutines share the main thread. Each delay() suspends one and frees the thread for the other.

All learning paths · All scenarios