Skip to content

Async Models — Notes#

Structured concurrency#

Tie child tasks to a parent scope so cancellation, errors, and lifetimes are predictable.

coroutineScope {
  val a = async { fetchA() }
  val b = async { fetchB() }
  a.await() + b.await()
}
// if either throws, both are cancelled; scope returns or throws.

Available in Kotlin, Swift, Java 21 (StructuredTaskScope), Python asyncio.TaskGroup.

Thread-per-request still works#

With Java virtual threads and a fast runtime, you can write straight-line blocking code and let the runtime multiplex on a few OS threads. Often the simplest answer for a microservice.

Watch for these#

  • Don't mix sync and async in a hot path (asyncio.run inside a sync handler).
  • Avoid global executors for unbounded work; bound parallelism per dependency.
  • Channels with unbounded buffers are pretend-backpressure; bound them.

Refs#

  • "Notes on structured concurrency, or: Go statement considered harmful" — Nathaniel Smith.
  • Java Concurrency in Practice (still relevant; virtual threads add a chapter).
  • Go memory model + goroutines paper.
  • "Reactive Streams" spec (reactivestreams.org).
  • Joe Armstrong: "Programming Erlang" (actors).