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

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

1.0 Junior🔥 242 комментариев
#Kotlin основы#Многопоточность и асинхронность

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

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

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

Способы создания корутин в Kotlin

В Kotlin корутины можно создавать несколькими способами, каждый из которых имеет свои особенности и области применения. Основные подходы делятся на две категории: строители корутин (coroutine builders) и расширенные механизмы.

1. Основные строители корутин

launch

Создает корутину, которая выполняется конкурентно и не возвращает результат (аналог fire-and-forget). Возвращает Job, которым можно управлять.

val job = CoroutineScope(Dispatchers.IO).launch {
    // Выполнение асинхронной операции
    delay(1000)
    println("Корутина завершена")
}
// Можем отменить корутину
job.cancel()

async

Создает корутину, которая возвращает результат (обернутый в Deferred<T>). Используется для параллельных вычислений.

val deferred = CoroutineScope(Dispatchers.Default).async {
    // Вычисление результата
    delay(500)
    return@async 42
}
// Получаем результат (может приостановить корутину)
val result = deferred.await()

runBlocking

Блокирует текущий поток до завершения корутины. Используется преимущественно в тестах или main-функциях.

fun main() = runBlocking {
    // Эта корутина блокирует поток
    launch {
        delay(1000)
        println("Inside runBlocking")
    }
    println("Main thread continues after delay")
}

2. Создание через области видимости (CoroutineScope)

Определение собственного scope

Рекомендуемый подход для структурного параллелизма.

class MyViewModel : ViewModel() {
    private val customScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
    
    fun doWork() {
        customScope.launch {
            // Работа в корутине
        }
    }
    
    fun cleanup() {
        customScope.cancel() // Отмена всех корутин в scope
    }
}

Использование готовых scope

В Android доступны встроенные scope:

  • viewModelScope (в ViewModel)
  • lifecycleScope (в LifecycleOwner)
class MyFragment : Fragment() {
    fun loadData() {
        viewLifecycleOwner.lifecycleScope.launch {
            // Автоматическая отмена при уничтожении Fragment
            val data = fetchData()
            updateUI(data)
        }
    }
}

3. Продвинутые способы создания

coroutineScope и supervisorScope

Создают дочерние scope внутри существующей корутины.

suspend fun parallelOperations() = coroutineScope {
    val deferred1 = async { fetchFromSource1() }
    val deferred2 = async { fetchFromSource2() }
    // Ожидаем оба результата
    deferred1.await() to deferred2.await()
}

Производитель-потребитель через produce

Создает корутину, которая производит поток данных.

fun produceNumbers() = CoroutineScope(Dispatchers.Default).produce {
    for (i in 1..10) {
        send(i)
        delay(100)
    }
    channel.close()
}

4. Низкоуровневое создание через конструктор

Менее распространенный, но максимально гибкий способ:

val continuation = suspend {
    // Тело suspend-функции
    "Результат"
}.createCoroutine(object : Continuation<String> {
    override val context = EmptyCoroutineContext
    override fun resumeWith(result: Result<String>) {
        println("Результат: ${result.getOrNull()}")
    }
})
continuation.resume(Unit)

Ключевые различия и рекомендации

  1. launch vs async:

    • Используйте launch для операций без возвращаемого значения
    • Используйте async для параллельных вычислений с результатом
  2. Выбор Dispatcher:

    • Dispatchers.Main - работа с UI
    • Dispatchers.IO - операции ввода-вывода
    • Dispatchers.Default - вычисления CPU-bound
  3. Обработка ошибок:

    • launch с CoroutineExceptionHandler
    • supervisorScope для изолированных ошибок
  4. Структурный параллелизм: Всегда создавайте корутины в определенной области видимости для предотвращения утечек памяти.

// Правильный подход - структурный параллелизм
suspend fun loadUserData(userId: String) = coroutineScope {
    val profile = async { api.getProfile(userId) }
    val friends = async { api.getFriends(userId) }
    UserData(profile.await(), friends.await())
}

Выбор способа создания корутины зависит от конкретной задачи: необходимости возврата результата, области видимости, обработки ошибок и требований к отмене операций.