Как запустить две задачи параллельно и дождаться их результата
Что проверяют
Понимаете ли вы структурированную конкурентность в корутинах Kotlin: где запускать задачи, как дождаться обеих и что будет, если одна упадёт. Следом почти всегда спрашивают, чем coroutineScope отличается от supervisorScope.
Как отвечать
Базовый код внутри suspend-функции: coroutineScope { val a = async { repo.loadUser() }; val b = async { repo.loadOrders() }; a.await() to b.await() }.
asyncиawait:asyncзапускает задачу сразу и возвращаетDeferred,await()приостанавливает корутину до результата. Для списка задач естьawaitAll();- где запускать: во
ViewModelэтоviewModelScope, в UIlifecycleScopeвместе сrepeatOnLifecycle. Корутины отменяются вместе со скоупом, и после закрытия экрана запросы не работают впустую; - ошибки: в
coroutineScopeисключение одной задачи отменяет вторую и пробрасывается наверх. Если нужен хотя бы один результат, берутsupervisorScopeи оборачивают каждыйawait()вtry/catchилиrunCatching; - диспетчеры:
asyncнаследует диспетчер родителя. Suspend-функции Retrofit и Room безопасны для главного потока, а блокирующий код уводят вwithContext(Dispatchers.IO), иначе наDispatchers.Mainзадачи выполнятся по очереди и подвесят интерфейс; - тесты:
runTestизkotlinx-coroutines-testпроматываетdelayв виртуальном времени, и задачи с задержками в одну и две секунды проверяются мгновенно.
Если спросят про альтернативы: в RxJava это Single.zip, для потоков данных combine у Flow, в Java CompletableFuture.allOf.
Где спотыкаются
Пишут val a = async { ... }.await() и следом val b = async { ... }.await(). Каждая задача ждётся сразу после запуска, и выполнение становится последовательным.
Берут runBlocking в Android-коде или GlobalScope. Первый блокирует главный поток и ведёт к ANR, второй создаёт корутины без владельца, которые переживают экран и не отменяются.
Проверяют результат через assert() в тестах. В Kotlin на JVM эта проверка срабатывает только с флагом -ea, по умолчанию она выключена, и тест проходит, ничего не проверив. Нужны assertEquals из JUnit или kotlin.test.