Корутины в Kotlin — это легковесные потоки с поддержкой приостановки и возобновления вычислений без блокирования потоков ОС. Запускаются через функции, такие как launch, async при наличии родительского CoroutineScope.
Диспетчеризация происходит через объекты CoroutineDispatcher, определяющие, в каком потоке/экзекьютере исполняется корутина (Dispatchers.Main, Dispatchers.IO, Dispatchers.Default).
Пример:
fun main() = runBlocking { launch(Dispatchers.IO) { val data = getDataFromNetwork() withContext(Dispatchers.Main) { updateUI(data) } } }
Отмена: Корутины отменяются через отмену их job/Scope, что бросает внутреннее исключение CancellationException. Корутины должны периодически проверять флаг отмены либо вызывать приостанавливающие функции, чтобы корректно завершиться.
Обработка ошибок: Исключения в корутинах могут "тонуть" — например, если вы не обработаете ошибку в дочерней корутине, то она только завершит свою работу, а родитель не узнает о проблеме. Для этого есть механизмы SupervisorJob и CoroutineExceptionHandler.
Нюансы передачи контекста:
Что произойдет, если внутри родительской корутины одна из дочерних завершится с ошибкой, а другие продолжают работать? Будут ли отменены все дочерние?
Многие ошибочно полагают, что ошибка "остается в дочерней".
Правильный ответ: Если используется обычный Job (или launch), то все дочерние корутины автоматически отменяются при ошибке любой дочерней. Для того чтобы дочерние корутины не отменялись, используется SupervisorJob или supervisorScope:
supervisorScope { launch { error("fail") } launch { println("Этот код будет работать") } }
История
Неправильная обработка ошибок — падение флоу: В проекте использовали корутины для загрузки данных. Исключения "тонули" внутри дочерних корутин, из-за чего уровень retry/recovery не работал, и главный экран оставался пустым без каких-либо сообщений об ошибке для пользователя.
История
Зависание UI из-за работы с Dispatchers.Main: Молодой Android-разработчик запустил тяжёлую вычислительную задачу на Dispatchers.Main — UI начал "замерзать". Не было понимания, что любые тяжёлые вычисления должны исполняться на Dispatchers.Default или Dispatchers.IO.
История
AbortError при отмене родительского scope: Один из разработчиков не учёл, что при отмене родительского скоупа все дочерние корутины завершаются отменой, и критически важная операция (сохранение данных) не была завершена.