Перейти к содержанию
Innovialabs

Корутины в Kotlin: практическое введение

Практика3 мин

Коротко

Корутины в Kotlin позволяют писать асинхронный код последовательно: suspend-функции не блокируют поток, launch и async запускают работу в scope, а отмена идёт по иерархии автоматически.

Что такое корутины и зачем они нужны

Корутина — это вычисление, которое может приостановиться и продолжиться позже, не блокируя поток. В Kotlin это позволяет писать сетевые запросы и работу с базой как обычный последовательный код, без колбэков:

suspend fun loadProfile(id: String): Profile {
    val user = api.getUser(id)       // приостановка, поток свободен
    val orders = api.getOrders(id)
    return Profile(user, orders)
}

Корутины живут в библиотеке kotlinx.coroutines. На Android главный поток при этом не зависает, а интерфейс остаётся отзывчивым.

suspend-функции

Модификатор suspend означает: функция может приостановиться. Вызвать её можно только из другой suspend-функции или из корутины.

Важно понимать:

  • suspend не делает функцию фоновой сама по себе. Тяжёлую работу всё равно нужно перенести на подходящий диспетчер.
  • Хорошая suspend-функция main-safe: её можно безопасно вызвать из главного потока, потому что она сама переключает контекст.
suspend fun readFile(path: String): String =
    withContext(Dispatchers.IO) {
        File(path).readText()
    }

launch или async

launchasync
ВозвращаетJobDeferred<T>
РезультатНе нуженПолучается через await()
Когда применять«Сделай и забудь»: сохранить, отправитьПараллельные запросы с результатом

Параллельная загрузка двух ресурсов:

suspend fun loadScreen(id: String) = coroutineScope {
    val user = async { api.getUser(id) }
    val orders = async { api.getOrders(id) }
    Screen(user.await(), orders.await())
}

Если один из запросов упадёт, второй будет отменён, а ошибка дойдёт до вызывающего кода.

Диспетчеры: где выполняется код

  • Dispatchers.Main — главный поток UI (Android, desktop).
  • Dispatchers.IO — сеть, файлы, база данных: блокирующие операции ввода-вывода.
  • Dispatchers.Default — вычисления, нагружающие процессор: парсинг, сортировка, обработка изображений.

Переключайтесь через withContext, а не через запуск новых корутин. Многие сетевые библиотеки, например Retrofit, сами предоставляют suspend-функции, и оборачивать их вызов в Dispatchers.IO не требуется.

Структурированная конкурентность

Каждая корутина запускается внутри CoroutineScope, и это главный принцип:

  • Родитель ждёт завершения всех детей.
  • Ошибка ребёнка отменяет родителя и остальных детей.
  • Отмена scope отменяет всё внутри.

На Android используйте готовые scope: viewModelScope во ViewModel и lifecycleScope в Activity и Fragment. Они отменяются вместе с компонентом, поэтому запросы не продолжают работу после закрытия экрана.

class ProfileViewModel : ViewModel() {
    fun load(id: String) {
        viewModelScope.launch {
            try {
                _state.value = UiState.Data(repo.loadProfile(id))
            } catch (e: IOException) {
                _state.value = UiState.Error
            }
        }
    }
}

Избегайте GlobalScope: корутины в нём не привязаны к жизненному циклу и легко утекают.

Отмена

Отмена в корутинах кооперативная: код должен в ней участвовать.

  • Все suspend-функции из kotlinx.coroutines (delay, withContext и другие) проверяют отмену сами.
  • В долгих циклах вызывайте ensureActive() или проверяйте isActive.
  • Не глотайте CancellationException в общем catch (e: Exception) — пробрасывайте её дальше.
  • Для освобождения ресурсов используйте finally, а если там нужно вызвать suspend-функцию — withContext(NonCancellable).

Таймаут на сетевой запрос:

val result = withTimeoutOrNull(5_000) { api.getUser(id) }

Частые ошибки

  • runBlocking в продакшен-коде блокирует поток — он нужен в main и тестах.
  • Запуск корутины без scope или в GlobalScope.
  • Тяжёлые вычисления на Dispatchers.Main, из-за которых интерфейс подвисает.
  • async без await: ошибка может потеряться или всплыть неожиданно.

Частые вопросы

Нет. Корутины выполняются на потоках, но одна корутина может продолжиться на другом потоке после приостановки. Тысячи корутин способны работать на небольшом пуле потоков.

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.