Skip to content

async/await Syntax

"What color is your function?" — the famous framing for why marking a function async isn't cosmetic: it changes the function's type, forces every caller to also become async (or explicitly bridge the gap), and this "coloring" propagates through an entire codebase from a single leaf-level async call.

flowchart LR Junior["Junior: what changes when you add async to a function signature"] --> Middle["Middle: function coloring and why it propagates"] Middle --> Senior["Senior: bridging sync and async code deliberately"] Senior --> Professional["Professional: why some languages rejected this trade-off entirely (Go)"]
flowchart LR Leaf["Leaf function becomes\nasync (needs to await\nsomething)"] --> Caller1["Its caller must ALSO\nbecome async"] Caller1 --> Caller2["THAT caller must ALSO\nbecome async"] Caller2 --> AllTheWay["... propagates ALL\nTHE WAY UP the call\nchain"]

Choose a level

Level Guide You are done when
Junior What changes with async You can explain what an async fn's return type actually is under the hood.
Middle Function coloring You can explain why one async call forces every caller up the chain to also become async.
Senior Bridging sync and async deliberately You can design a safe boundary between synchronous and asynchronous code.
Professional Why Go rejected this trade-off You can explain Go's alternative (goroutines, no async keyword) and its own trade-offs.

Practice rule

Before adding async to a low-level utility function "just in case," consider that this decision propagates to every single caller, transitively, for the rest of that function's life in the codebase — function coloring is a one-way door that's expensive to reverse later.