В чем сходство и различия методов 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&lt;Int&gt; = 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 — для задач, где результат важен и будет использоваться позже, особенно в параллельных вычислениях.