HeadlinesBriefing favicon HeadlinesBriefing.com

Async evolution: from callbacks to async/await

Hacker News •
×

OS threads consume megabytes of stack and cost about a millisecond to spawn, making thousands‑thread servers waste memory and CPU. The C10K problem, coined by Dan Kegel in 1999, forced engineers to find concurrency models that avoided a thread per connection. Early solutions migrated I/O to event loops and callbacks, a pattern popularized by Node.js and Nginx.

Callbacks eliminated thread overhead but tangled control flow, spawning “callback hell” where each step required its own error branch and cancellation was impossible. The next wave introduced promises—objects representing a future value—standardized in JavaScript’s ES2015 and Java 8’s CompletableFuture. Promises allowed linear chaining and centralized .catch handling, yet remained single‑shot, struggled with streams, and forced developers to juggle mixed synchronous and asynchronous return types.

Async/await arrived in C# 2012 and spread to ES2017, Python 3.5, Rust 1.39, Kotlin and Swift, letting code appear sequential while the runtime manages non‑blocking I/O. This syntactic sugar removed the “function coloring” tax that plagued promise‑based APIs, making loops and try/catch natural. Today most high‑performance servers write their concurrency with async/await, proving the promise of async has finally been delivered.