Почему эти два вызова async всё равно занимают две секунды вместо одной?
Два независимых сетевых вызова, каждый примерно на секунду, обёрнуты в async, чтобы выполняться параллельно. Функция всё равно занимает около двух секунд.
Ограничения: вызовы независимы, loadUser и loadFeed — suspend-функции, а вся функция должна укладываться примерно в одну секунду.
suspend fun load(): Screen = coroutineScope {
val user = async { loadUser() }.await()
val feed = async { loadFeed() }.await()
Screen(user, feed)
}
Найдите и исправьте ошибку.
Немедленный await делает вызовы последовательными: первый async дожидаются раньше, чем стартовал второй, поэтому два секундных вызова идут подряд. Сначала запустите оба билдера, сохранив Deferred в переменные, и лишь потом их дождитесь.
- ✗Думать, что
asyncленив и стартует только наawait() - ✗Считать, что
await()блокирует поток, а не приостанавливает корутину - ✗Полагать, что два ребёнка одного scope наложатся независимо от места
await()
- →Что изменил бы здесь
awaitAll(user, feed)? - →Если
loadUser()бросит исключение, что станет с ещё работающимloadFeed()?
Баг
await() вызывается прямо на результате async, поэтому вторая корутина ещё даже не создана, когда первую уже ждут:
suspend fun load(): Screen = coroutineScope {
val user = async { loadUser() }.await() // ждём 1 c
val feed = async { loadFeed() }.await() // только теперь стартуем — ещё 1 c
Screen(user, feed)
}
async запускается сразу (по умолчанию не ленив), но .await() в той же строке приостанавливает вызывающую корутину до конца первого вызова. Параллелизма нет — есть два последовательных вызова.
Исправление
Сначала запускаем оба билдера, потом ждём:
suspend fun load(): Screen = coroutineScope {
val user = async { loadUser() } // стартовал
val feed = async { loadFeed() } // стартовал
Screen(user.await(), feed.await()) // ~1 c суммарно
}
Оба Deferred уже выполняются, когда доходит дело до await(), поэтому суммарное время равно времени более медленного вызова. coroutineScope дожидается обоих детей, а сбой любого из них отменит второго.