Какие знаешь способы запуска coroutine?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Основные способы запуска корутин в Kotlin
В Kotlin Coroutines существует несколько строительных функций (builder functions) для запуска корутин. Выбор конкретного способа зависит от требуемого поведения, обработки исключений и необходимости получения результата.
1. launch - Запуск "фоновой" корутины
Это наиболее распространённый способ для запуска задачи, результат которой не требуется немедленно или не требуется вообще (fire-and-forget). Функция возвращает объект Job, через который можно управлять жизненным циклом корутины (отмена, ожидание завершения).
Ключевые особенности:
- Не возвращает результат (возвращает
Job). - Необработанные исключения внутри
launchприводят к краху родительской корутины (и приложения, если не установленCoroutineExceptionHandler), если только она не запущена вSupervisorJob. - Используется для side-effect операций (логирование, обновление UI, отправка аналитики).
import kotlinx.coroutines.*
fun main() = runBlocking {
// Запуск в текущем CoroutineScope (runBlocking)
val job: Job = launch {
delay(1000L)
println("Корутина, запущенная через launch, завершила работу")
}
println("Основной поток продолжает работу немедленно")
job.join() // Ожидание завершения дочерней корутины
}
2. async - Запуск корутины с возвратом результата (Deferred)
Используется, когда необходимо выполнить вычисление асинхронно и получить его результат в будущем. Возвращает объект Deferred<T> (наследник Job), который содержит будущий результат типа T.
Ключевые особенности:
- Возвращает
Deferred<T>, из которого результат извлекается с помощьюawait(). - Исключения внутри
asyncНЕ выбрасываются немедленно, а откладываются до вызоваawait(). - Позволяет легко выполнять конкурентные (параллельные) вычисления.
import kotlinx.coroutines.*
fun main() = runBlocking {
// Параллельный запуск двух асинхронных задач
val deferredNumber: Deferred<Int> = async {
delay(500L)
42 // Последнее выражение - результат
}
val deferredString: Deferred<String> = async {
delay(300L)
"Hello"
}
// await() приостанавливает корутину, пока результат не будет готов
val number = deferredNumber.await()
val string = deferredString.await()
println("Результат: $string $number") // Результат: Hello 42
// Если не нужны промежуточные результаты, можно использовать awaitAll
val sum = awaitAll(async { 1 }, async { 2 }).sum()
println(sum) // 3
}
3. runBlocking - Блокирующий запуск
Эта функция создаёт новую корутину и блокирует текущий поток до её полного завершения. Используется главным образом в точках входа в программу (main), в тестах или для интеграции блокирующего кода в асинхронный мир.
Важное предупреждение: Её не следует использовать внутри обычных suspend-функций или корутин, так как это противоречит идее неблокирующей асинхронности.
import kotlinx.coroutines.*
fun main() { // Обычная функция main
// runBlocking блокирует основной поток до завершения своей лямбды
val result = runBlocking {
delay(1000L)
"Результат выполнен в блокирующей корутине"
}
println(result)
// До этой строки выполнение дойдёт только через 1 секунду.
}
4. coroutineScope и supervisorScope - Структурированный параллелизм
Эти suspend-функции используются для создания собственных областей видимости (scope) внутри suspend-функций. Они приостанавливают вызывающую корутину до завершения всех своих дочерних корутин.
coroutineScope: При ошибке в любом дочернемlaunchотменяет все остальные дочерние корутины и выбрасывает исключение.supervisorScope: При ошибке в дочернемlaunchне отменяет другие дочерние корутины. Исключение нужно обрабатывать внутри дочерней корутины.
import kotlinx.coroutines.*
suspend fun performParallelTasks() = coroutineScope {
// Все launch внутри этой области - дочерние корутины
launch { task1() }
launch { task2() }
// Функция performParallelTasks завершится только после task1 И task2
}
suspend fun task1() { delay(1000L) }
suspend fun task2() { delay(1500L) }
5. produce - Запуск корутины для асинхронных потоков данных
Специальный билдер, который создаёт корутину, возвращающую ReceiveChannel. Предназначен для реализации паттерна Producer-Consumer (поставщик-потребитель). В современных версиях библиотеки чаще рекомендуется использовать Flow.
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
val channel: ReceiveChannel<Int> = produce {
for (x in 1..5) {
send(x * x) // Отправляем данные в канал
delay(100L)
}
}
// Потребляем данные из канала
for (value in channel) {
println(value) // 1, 4, 9, 16, 25
}
}
Итог и критерии выбора
launch— для фоновой задачи без результата.async— для асинхронного вычисления с результатом, особенно при параллельных операциях.runBlocking— только для перехода из синхронного мира в асинхронный (main, тесты).coroutineScope/supervisorScope— для структурирования кода внутри suspend-функций, требующих параллельного выполнения.produce— для создания асинхронных потоков данных через каналы (чаще заменяется наFlow).
Все эти строители требуют CoroutineScope для запуска, который обеспечивает управление жизненным циклом и отменой, реализуя принцип структурированного параллелизма — ключевую концепцию корутин, гарантирующую отсутствие утечек и корректную отмену операций.