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

Как дождаться завершения работы корутины

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

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

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

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

В Android разработке с Kotlin ожидание завершения работы корутины — это фундаментальная задача, особенно при интеграции асинхронного кода с синхронными API или при необходимости гарантировать выполнение определённых операций перед продолжением. Вот основные методы и подходы.

Основные методы ожидания завершения корутины

1. Использование функции runBlocking

runBlocking — это функция, которая блокирует текущий поток до завершения всех корутин внутри её блока. Она не рекомендуется для использования в Android UI-потоке (Main thread), так как может привести к блокировке интерфейса и ANR (Application Not Responding). Однако она идеальна для тестов или скриптов.

import kotlinx.coroutines.*

fun main() {
    runBlocking {
        val job = launch {
            delay(1000L)
            println("Корутина завершилась")
        }
        // runBlocking автоматически дожидается завершения job
    }
    println("Основной поток продолжает работу")
}

2. Использование Job.join()

Каждая корутина возвращает объект Job. Вызов метода join() на этом объекте приостанавливает текущую корутинную область (coroutine scope) до завершения целевой корутины, но не блокирует поток.

import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        delay(2000L)
        println("Задача выполнена")
    }
    
    println("Ожидание завершения корутины...")
    job.join() // Приостанавливаем эту корутинную область до завершения job
    println("Основная корутина продолжает работу после join")
}

3. Использование Deferred.await()

Если корутина запускается с помощью async, она возвращает Deferred (расширенный Job), который представляет собой будущий результат. Метод await() приостанавливает корутинную область и возвращает результат вычисления.

import kotlinx.coroutines.*

fun main() = runBlocking {
    val deferredResult: Deferred<Int> = async {
        delay(1000L)
        42 // Возвращаем результат
    }
    
    println("Ожидание результата...")
    val result = deferredResult.await() // Приостанавливаем и получаем значение
    println("Результат: $result")
}

4. Комбинация нескольких корутин с awaitAll

Для ожидания завершения группы корутин можно использовать awaitAll() на списке Deferred.

import kotlinx.coroutines.*

fun main() = runBlocking {
    val deferredList = listOf(
        async { delay(1000); "Результат 1" },
        async { delay(1500); "Результат 2" },
        async { delay(500);  "Результат 3" }
    )
    
    val results = deferredList.awaitAll()
    println("Все результаты готовы: $results")
}

Ключевые концепции и предостережения для Android

Scope и жизненный цикл

В Android крайне важно использовать правильный CoroutineScope, связанный с жизненным циклом компонента (например, viewModelScope в ViewModel или lifecycleScope в Activity/Fragment). Ожидание завершения корутины внутри такого scope должно учитывать возможную его cancellation (например, при выходе пользователя из экрана).

// В ViewModel
class MyViewModel : ViewModel() {
    fun performTask() {
        viewModelScope.launch {
            val result = async { heavyComputation() }.await()
            updateUI(result)
        }
    }
    
    private suspend fun heavyComputation(): Int = withContext(Dispatchers.Default) {
        delay(3000)
        return 100
    }
}

Обработка исключений и timeout

При ожидании завершения корутин необходимо обрабатывать исключения. Также можно использовать функции с timeout.

import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        try {
            delay(5000L)
            println("Успешное завершение")
        } catch (e: CancellationException) {
            println("Корутина была cancelled")
        }
    }
    
    // Ожидаем максимум 2 секунды
    withTimeout(2000L) {
        job.join()
    }
}

Никогда не блокировать Main Thread

Самое важное правило: никогда используйте runBlocking или явные блокирующие вызовы на Main Thread в Android приложении. Это приведёт к заморозке UI и ANR. Все ожидания должны быть suspend (приостанавливающими) и выполняться внутри корутинной области, которая работает на соответствующем dispatcher (например, Dispatchers.IO для тяжелых операций).

// ПРАВИЛЬНО: использование suspend функций и корутин в UI контексте
lifecycleScope.launch {
    // Не блокирует UI, так как является suspend вызовом
    val data = fetchDataFromNetwork() // suspend функция
    updateUI(data)
}

// НЕПРАВИЛЬНО: блокировка Main Thread
// runBlocking { // ЗАПРЕЩЕНО в Activity/Fragment
//     fetchDataBlocking()
// }

Вывод

Ожидание завершения корутины в Kotlin осуществляется через:

  • Приостанавливающие функции (join(), await()), которые не блокируют поток.
  • Использование структурных конкурентности (runBlocking только для специальных случаев).
  • Учёт scope и жизненного цикла в Android для предотвращения утечек ресурсов и корректной cancellation.

Всегда предпочитайте suspend подходы и корректные scopes для создания надежных и отзывчивых Android приложений.

Как дождаться завершения работы корутины | PrepBro