Какие знаешь способы создания корутины?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Способы создания корутин в 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)
Ключевые различия и рекомендации
-
launch vs async:
- Используйте
launchдля операций без возвращаемого значения - Используйте
asyncдля параллельных вычислений с результатом
- Используйте
-
Выбор Dispatcher:
- Dispatchers.Main - работа с UI
- Dispatchers.IO - операции ввода-вывода
- Dispatchers.Default - вычисления CPU-bound
-
Обработка ошибок:
launchсCoroutineExceptionHandlersupervisorScopeдля изолированных ошибок
-
Структурный параллелизм: Всегда создавайте корутины в определенной области видимости для предотвращения утечек памяти.
// Правильный подход - структурный параллелизм
suspend fun loadUserData(userId: String) = coroutineScope {
val profile = async { api.getProfile(userId) }
val friends = async { api.getFriends(userId) }
UserData(profile.await(), friends.await())
}
Выбор способа создания корутины зависит от конкретной задачи: необходимости возврата результата, области видимости, обработки ошибок и требований к отмене операций.