В чем отличие launch от async в Kotlin Coroutines
Что проверяют
Понимаете ли вы разницу в возвращаемом значении и, главное, в обработке исключений.
Как отвечать
Оба это построители корутин, вызываемые на области видимости, и различий три.
Результат. launch возвращает Job и работает по принципу «запустил и забыл»: результат он не отдаёт. async возвращает Deferred<T>, у которого есть await(), отдающий значение.
Назначение. launch для действия (отправить событие, обновить состояние), async для параллельного получения нескольких результатов:
val user = async { api.user() }
val jobs = async { api.jobs() }
render(user.await(), jobs.await())Обработка исключений, и это самое важное. У launch исключение выбрасывается сразу и уходит вверх по иерархии: отменяет родителя и попадает в CoroutineExceptionHandler области. У async исключение сохраняется в Deferred и выбрасывается при await(). Поэтому обрабатывать его надо в try/catch вокруг await, а не вокруг самого async.
Тонкость: то, что исключение отложено, не значит, что оно безобидно. Под обычным Job сбой в async всё равно отменит родителя по правилам структурной конкурентности. А вот под SupervisorJob или в supervisorScope результат async, на который никто не вызвал await, действительно может пропасть незамеченным.
Практическая ошибка, которую стоит назвать: async { }.await() подряд одной строкой. Это не параллельность, а обычный последовательный вызов с лишней обёрткой. Смысл появляется только тогда, когда обе корутины запущены до первого await.
И launch, и async подчиняются структурной конкурентности: они привязаны к области, отмена области отменяет их, а родитель ждёт завершения детей.
Где спотыкаются
Оборачивают try/catch вокруг async вместо await.
Пишут async {}.await() и теряют параллельность.
Используют async там, где результат не нужен.
Считают, что отложенное исключение никогда не уронит родителя.