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

Что будет если использовать try/catch снаружи launch в Coroutines?

2.7 Senior🔥 171 комментариев
#Kotlin основы#Многопоточность и асинхронность

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

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

Использование try/catch вне launch в Kotlin Coroutines

Когда вы используете try/catch блок снаружи launch-билдера в корутинах, это приводит к поведению, которое часто вызывает недоразумения у разработчиков. Давайте подробно разберем эту ситуацию.

Основной принцип работы

Ключевой момент: try/catch снаружи launch НЕ перехватывает исключения, возникшие внутри корутины. Это происходит потому, что launch создает асинхронную операцию, которая выполняется в отдельном контексте, и исключения внутри нее не пробрасываются напрямую в окружающий код.

import kotlinx.coroutines.*

fun main() {
    try {
        // Внешний try/catch
        GlobalScope.launch {
            // Исключение внутри корутины
            throw RuntimeException("Ошибка внутри корутины!")
        }
    } catch (e: Exception) {
        // Этот блок НЕ СРАБОТАЕТ!
        println("Исключение перехвачено: ${e.message}")
    }
    
    // Даем время для выполнения корутины
    Thread.sleep(1000)
    println("Программа завершена")
}

В этом примере исключение НЕ БУДЕТ перехвачено внешним try/catch, и приложение либо завершится с ошибкой (в консольном приложении), либо упадет (в Android).

Почему так происходит?

  1. Асинхронная природа: launch запускает корутину, которая выполняется асинхронно относительно текущего потока
  2. Раздельные контексты выполнения: Корутина работает в своем собственном контексте, и исключения внутри нее не распространяются за его пределы
  3. Отложенное выполнение: Корутина может начать выполнение уже после того, как внешний try/catch блок завершил свою работу

Правильные подходы к обработке исключений

1. Использование try/catch внутри корутины

GlobalScope.launch {
    try {
        throw RuntimeException("Ошибка внутри корутины!")
    } catch (e: Exception) {
        println("Исключение перехвачено внутри: ${e.message}")
    }
}

2. Использование CoroutineExceptionHandler (для launch)

val exceptionHandler = CoroutineExceptionHandler { _, exception ->
    println("Обработано исключение: ${exception.message}")
}

GlobalScope.launch(exceptionHandler) {
    throw RuntimeException("Ошибка с обработчиком!")
}

3. Использование async/await (для перехвата исключений в вызывающем коде)

import kotlinx.coroutines.*

fun main() = runBlocking {
    val deferred = GlobalScope.async {
        throw RuntimeException("Ошибка в async!")
    }
    
    try {
        deferred.await() // Исключение будет проброшено здесь
    } catch (e: Exception) {
        println("Исключение перехвачено: ${e.message}")
    }
}

Особенности для Android

В Android-приложениях неперехваченные исключения в корутинах приводят к:

  • Крах приложения, если исключение происходит в корутине без должной обработки
  • Поведение зависит от области видимости:
    • GlobalScope - исключение может привести к завершению всего приложения
    • viewModelScope / lifecycleScope - исключение отменяет только текущую корутину, но не влияет на другие

Рекомендации по обработке исключений

  1. Всегда обрабатывайте исключения внутри корутины с помощью внутреннего try/catch
  2. Используйте CoroutineExceptionHandler для централизованной обработки ошибок
  3. Для структурированного параллелизма используйте supervisorScope, который позволяет дочерним корутинам завершаться независимо
  4. Тестируйте сценарии с исключениями, чтобы убедиться в корректности обработки
// Пример с supervisorScope
suspend fun processItems() = supervisorScope {
    val job1 = launch {
        try {
            // Потенциально опасная операция
        } catch (e: Exception) {
            // Локальная обработка
        }
    }
    
    val job2 = launch {
        // Даже если здесь произойдет исключение, job1 продолжит работу
    }
}

Вывод

Использование try/catch снаружи launch является антипаттерном в работе с корутинами, поскольку не обеспечивает надежной обработки исключений. Для корректной работы необходимо использовать либо внутренние блоки try/catch, либо специальные механизмы Kotlin Coroutines для обработки ошибок. Это фундаментальное отличие от синхронного кода, которое важно понимать при работе с асинхронными операциями в Kotlin.