В чем сходство и различия методов launch и async?
Методы launch и async в Kotlin Coroutines — это два способа запуска корутин, но с разными целями и поведением.
Оба метода создают новую корутину, но их главное различие — в типе возвращаемого результата:
-
launch используется для запуска задач, не возвращающих результат (аналог void).
-
async используется для выполнения задач с возвратом результата (аналог Future или Promise).
🔁 Общее между launch и async
-
Оба запускают корутину в указанном контексте (CoroutineScope и CoroutineContext).
-
Оба не блокируют поток.
-
Оба могут быть отменены с помощью возвращаемого Job (у launch) или Deferred (у async, он наследует Job).
-
Оба могут вызывать suspend-функции внутри себя.
🔀 Различия между launch и async
Характеристика | launch | async |
---|---|---|
Возвращаемое значение | Job | Deferred<T> |
--- | --- | --- |
Используется для | Побочных эффектов, fire-and-forget | Получения результата, отложенных вычислений |
--- | --- | --- |
Получение результата | Нет результата | Через await() |
--- | --- | --- |
Обработка исключений | Через CoroutineExceptionHandler | Через try-catch при вызове await() |
--- | --- | --- |
Поведение по умолчанию | Запускается сразу | Тоже запускается сразу (но await может быть отложенным) |
--- | --- | --- |
Пример использования launch
launch {
println("Начали")
delay(1000)
println("Завершили")
}
-
Ничего не возвращает.
-
Выполняется асинхронно, результат игнорируется.
-
Удобно для обновления UI, логов, отправки данных и других побочных эффектов.
Пример использования async
val deferred: Deferred<Int> = async {
delay(1000)
42
}
val result = deferred.await()
println(result) // 42
-
async возвращает Deferred<Int>.
-
await() приостанавливает выполнение до готовности результата.
-
Удобно использовать, когда нужно выполнить параллельно несколько задач и объединить результат.
🧠 Когда async становится полезным
При параллельном выполнении нескольких вычислений:
val a = async { getA() }
val b = async { getB() }
val sum = a.await() + b.await()
Если бы использовался launch, то нельзя было бы легко сложить результаты.
⚠️ Особенности и подводные камни
1. async без await() — ловушка
Если async вызван, но await() не вызван — исключения не обрабатываются:
val d = async { error("Ошибка!") }
// ошибка «пропадёт», если не вызвать d.await()
Решение — всегда вызвать await() или использовать CoroutineScope.async внутри supervisorScope, чтобы ловить ошибки.
2. launch — лучше для "огненных" задач (fire-and-forget)
launch {
sendAnalytics()
updateUI()
}
Побочные эффекты, логирование, обновление данных — хорошее применение launch.
3. async — лучше для результатов
val usersDeferred = async { loadUsers() }
val settingsDeferred = async { loadSettings() }
val users = usersDeferred.await()
val settings = settingsDeferred.await()
Это даёт параллельное выполнение и ожидание результата позже.
✅ Кратко: когда что использовать
-
Хочешь что-то сделать → launch
-
Хочешь что-то вернуть → async
-
Обработка побочных эффектов → launch
-
Параллельные вычисления с результатом → async
Таким образом, launch и async — это разные инструменты для разных задач. launch — для действий без результата, async — для задач, где результат важен и будет использоваться позже, особенно в параллельных вычислениях.