Why do these two async calls still take two seconds instead of one?
Two independent network calls, each taking about one second, are wrapped in async so that they run in parallel. The function still takes about two seconds.
Constraints: the two calls are independent, loadUser and loadFeed are suspend functions, and the whole function must finish in about one second.
suspend fun load(): Screen = coroutineScope {
val user = async { loadUser() }.await()
val feed = async { loadFeed() }.await()
Screen(user, feed)
}
Find and fix the bug.
Awaiting on the spot serializes them: the first async is awaited before the second even starts, so the two second-long calls run back to back. Start both builders first, keep the Deferreds in variables, and await them afterwards.
- ✗Thinking
asyncis lazy and only starts whenawait()is called - ✗Believing
await()blocks the thread rather than suspending the coroutine - ✗Assuming two children of one scope overlap no matter where you await them
- →What would
awaitAll(user, feed)change about this code? - →If
loadUser()throws, what happens to the still-runningloadFeed()?
The bug
await() is called directly on the result of async, so the second coroutine does not even exist yet while the first one is being awaited:
suspend fun load(): Screen = coroutineScope {
val user = async { loadUser() }.await() // wait 1 s
val feed = async { loadFeed() }.await() // only now start — another 1 s
Screen(user, feed)
}
async does start eagerly (it is not lazy by default), but the .await() on the same line suspends the caller until that first call finishes. There is no parallelism — only two sequential calls.
The fix
Start both builders first, await afterwards:
suspend fun load(): Screen = coroutineScope {
val user = async { loadUser() } // started
val feed = async { loadFeed() } // started
Screen(user.await(), feed.await()) // ~1 s in total
}
Both Deferreds are already running by the time await() is reached, so the total time is that of the slower call. coroutineScope waits for both children, and a failure in either cancels the other.