Async and await syntax looks identical across modern languages, but under the hood, the underlying execution models diverge sharply. A study of seven popular async runtimes revealed that identical straight-line asynchronous code produces four completely different outputs depending on the language.
The variations stem from how runtimes handle task invocation and scheduling. Some languages employ hot calls, starting function execution synchronously until reaching the first await point. Others use cold calls, where invoking an async function merely constructs a suspended state machine that does not execute until spawned or awaited.
Spawning background tasks creates even larger discrepancies. In certain runtimes, an unawaited background task is scheduled eagerly on an event loop or thread pool. In others, leaving a task unawaited drops it entirely or delays its execution until the parent scope resolves. Across three variations of a tiny background logging program, no two of the seven runtimes produced the same execution trace.
Assuming that async semantics transfer across language boundaries is an easy way to introduce severe concurrency bugs.















