A suspend fun is an async function with implicit awaits; Deferred<T> is Promise<T>, and awaitAll is Promise.all. The difference is ownership: nothing can stop a running Promise, while a coroutine belongs to a scope that cancels it. The same race in Node 22 and Kotlin:
const t0 = Date.now(), log = (m) => console.log(`${Date.now() - t0} ms ${m}`);
const price = (ms, p) => new Promise((ok) => setTimeout(() => ok(p), ms));
const offline = (ms) => new Promise((_, no) => setTimeout(() => no(Error("offline")), ms));
const slow = price(400, 39.5).then(() => log("slow request still finished"));
await Promise.all([slow, offline(100)]).catch((e) => log(`caught ${e.message}`));Output
102 ms caught offline 403 ms slow request still finished
import kotlinx.coroutines.*
suspend fun price(ms: Long, p: Double): Double { delay(ms); return p }
fun main(): Unit = runBlocking {
log("start")
runCatching {
coroutineScope {
launch { price(400, 39.5); log("slow request still finished") }
launch { delay(100); throw java.io.IOException("offline") }
}
}.onFailure { log("caught ${it.message}; the slow request was cancelled") }
}Output
0 ms [main @coroutine#1] start 291 ms [main @coroutine#1] caught offline; the slow request was cancelled
Node finished the orphaned request after the error was handled; Kotlin cancelled it (its 291 ms includes loading classes on first use). In JavaScript you would pass an AbortController signal to every call.