← Назад к вопросам

Отменится async при выбросе исключения если внутри async лежит launch в корутинах

1.7 Middle🔥 131 комментариев
#Многопоточность и асинхронность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Отмена async при исключении в вложенном launch

Короткий ответ

Нет, async не отменится, если исключение выброшено в вложенном launch. Это ключевая разница в обработке исключений между async и launch.

Почему?

launch и async имеют разные стратегии обработки исключений:

launch — пробрасывает исключения вверх (propagates exceptions)

  • Необработанное исключение отменяет всю корутину
  • Отменяет всех родителей и sibling корутины

async — захватывает исключения в результат (suspends exceptions)

  • Исключение не пробрасывается автоматически
  • Исключение доступно только при вызове await()
  • Вложенный launch не может отменить родительский async

Пример 1: launch внутри async

scope.async {
    println("Async начался")
    
    launch {
        println("Launch начался")
        throw Exception("Ошибка в launch")
        println("Эта строка не выполнится")
    }
    
    println("Async продолжает работу после launch")
}.await()  // Не выбросит исключение!

// Вывод:
// Async начался
// Launch начался
// Async продолжает работу после launch
// Исключение потеряно!

Почему async не отменится?

val job = scope.async {
    // async = Deferred<T>
    // Исключения находятся внутри результата
    
    launch {
        // launch = Job
        // Исключения пробрасываются наружу
        // Но async их не слушает!
        throw Exception()
    }
    
    // async продолжает работу
    "Результат"
}

job.await()  // Вернёт "Результат", исключение потеряно

Пример 2: Правильная обработка

scope.async {
    try {
        launch {
            throw Exception("Ошибка")
        }.join()  // Ждём завершения launch
    } catch (e: Exception) {
        println("Поймали: ${e.message}")
    }
}.await()

// Или лучше:
scope.async {
    val result = supervisorScope {
        launch {
            throw Exception("Ошибка")
        }
    }
}

Пример 3: Что ДЕЙСТВИТЕЛЬНО отменит async

// Исключение в самом async
scope.async {
    throw Exception("Ошибка здесь")
}.await()  // Выбросит исключение!

// launch не может отменить async, но:
val deferred = scope.async { ... }
deferred.cancel()  // Явная отмена

Иерархия корутин

scope (parent)
  ├─ async (captures exceptions)
  │   └─ launch (propagates exceptions)
  │       └─ Exception выброшена
  │
  // launch не может отменить async!
  // launch отменится, но async продолжит работу

Правила обработки исключений

Ситуацияlaunchasync
Exception в корутинеОтменяет parentСохраняется в Deferred
Вложенный launch выброситОтменяет всехНе влияет на async
Вложенный async выброситТребует await()Не влияет
try-catch работает✅ Да✅ Да

CoroutineExceptionHandler

val handler = CoroutineExceptionHandler { context, exception ->
    println("Перехвачено: ${exception.message}")
}

scope.async(handler) {
    launch {
        throw Exception("В launch")
    }
    // handler НЕ сработает! Exception в launch, не в async
}.await()

// Для launch это работает:
scope.launch(handler) {
    throw Exception()
    // handler СРАБОТАЕТ!
}

Вывод

async НЕ отменится, если исключение выброшено в вложенном launch, потому что:

  1. async захватывает исключения в результат
  2. launch пробрасывает исключения вверх
  3. async не слушает исключения от launch
  4. Нужна явная обработка через try-catch или .join()

Рекомендация: если важно обработать исключение из launch, используй try-catch или supervisorScope.

Отменится async при выбросе исключения если внутри async лежит launch в корутинах | PrepBro