> Какие проблемы возникают при оборачивании launch в try-catch в Kotlin Coroutines (Kotlin, Android)
Уровень: senior · Роль: mobile · Категория: Технические вопросы
Компании: Aston
Стек: Kotlin, Android
> Пример ответа
Короткий ответ
Проблема в том, что launch - это функция, которая возвращает Job и запускает корутину асинхронно, но сам вызов launch не выполняет тело корутины синхронно. Исключения, возникающие внутри корутины, не пробрасываются в вызывающий код через try-catch. Они распространяются через CoroutineExceptionHandler или отменяют родительскую корутину. try-catch вокруг launch ловит только ошибки при создании корутины, но не ошибки внутри неё.
Подробное объяснение
launch - это builder корутины, который возвращает Job немедленно. Тело корутины выполняется в диспетчере, часто в другом потоке или после переключения контекста. Поэтому исключение внутри тела корутины не может быть перехвачено try-catch в месте вызова launch - к моменту возникновения исключения стек вызовов уже другой.
Исключения в корутинах, запущенных через launch, обрабатываются следующим образом:
- если есть
CoroutineExceptionHandlerв контексте - он получит исключение; - если нет - исключение передаётся в родительскую корутину, что приводит к отмене всей иерархии;
- в Android это часто приводит к крашу приложения, так как исключение доходит до глобального обработчика.
Дополнительная проблема: try-catch вокруг launch может создать ложное ощущение безопасности. Разработчик думает, что ошибки обработаны, но на самом деле они уходят в глобальный обработчик. Это особенно опасно в production, где краши маскируются и сложно диагностируются.
Также стоит учитывать: если внутри корутины есть несколько точек с исключениями, try-catch снаружи не поможет ни для одной из них. Нужно использовать try-catch внутри корутины, либо runCatching, либо supervisorScope с обработкой ошибок на уровне дочерних корутин.
На практике
На Android это проявляется в виде необработанных исключений в фоновых задачах. Например, при загрузке данных из сети внутри launch - если сервер вернёт ошибку, приложение упадёт, несмотря на try-catch вокруг launch.
Правильный подход - обрабатывать исключения внутри корутины:
- обернуть тело корутины в
try-catch; - использовать
CoroutineExceptionHandlerдля глобальной обработки; - для параллельных задач использовать
supervisorScope+asyncсawaitвнутри try-catch; - для UI-событий использовать
viewModelScope.launchс обработкой ошибок внутри.
Также важно помнить: launch не возвращает результат. Если нужно получить результат или обработать ошибку - использовать async + await, где исключение пробрасывается в месте await и может быть перехвачено try-catch.
Пример кода
// Неправильно: try-catch не перехватит исключение из корутины fun loadData() { try { viewModelScope.launch { val data = api.fetchData() // здесь может быть IOException updateUi(data) } } catch (e: Exception) { // этот блок не выполнится для ошибок внутри корутины showError(e) } } // Правильно: try-catch внутри корутины fun loadData() { viewModelScope.launch { try { val data = api.fetchData() updateUi(data) } catch (e: Exception) { showError(e) } } } // Альтернатива: async + await fun loadData() { viewModelScope.launch { try { val data = async { api.fetchData() }.await() updateUi(data) } catch (e: Exception) { showError(e) } } }
Как отвечать на собеседовании
Начни с главного: launch не выполняет тело синхронно, поэтому try-catch снаружи бесполезен. Затем объясни механизм распространения исключений в корутинах: CoroutineExceptionHandler, отмена родительской корутины, глобальный обработчик. Приведи пример из практики - краш приложения при сетевой ошибке. Покажи правильные паттерны: try-catch внутри корутины, async + await, supervisorScope. Упомяни разницу между launch и async в контексте обработки ошибок. Если спросят про SupervisorJob - объясни, как он изолирует ошибки дочерних корутин.
Что проверяет интервьюер
- понимание асинхронной природы корутин;
- знание механизма распространения исключений в иерархии корутин;
- умение выбирать правильный инструмент для обработки ошибок;
- понимание разницы между
launchиasync; - знание
CoroutineExceptionHandlerиsupervisorScope; - способность объяснить практические последствия неправильной обработки.
Типичные ошибки
- утверждение, что try-catch вокруг
launchработает; - игнорирование
CoroutineExceptionHandler; - использование
GlobalScopeбез обработки ошибок; - путаница между
launchиasyncпри обработке исключений; - забывание про отмену родительской корутины при необработанном исключении;
- попытка перехватить исключение после
launchчерезjob.join()без try-catch внутри корутины.
> Похожие задачи по mobile
Какой диспетчер использовать для загрузки ресурса с сети в Kotlin корутинах
Как работает компиляция в Kotlin Multiplatform
В чем разница между Int и Int? в Kotlin и когда Int рассматривается как объект, а когда как примитив
Как избежать ошибок при использовании lateinit var в Kotlin
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью