Как работает async вместе с await в корутинах?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Механизм async и await в Kotlin Coroutines
async и await — это ключевые строительные блоки для параллельного выполнения и асинхронных вычислений в корутинах Kotlin. Они работают в связке, позволяя запускать задачи конкурентно и легко получать их результаты.
Основная концепция
async — это функция-билдер корутин, которая запускает новую корутину и возвращает объект Deferred<T> (отложенное значение). Deferred — это аналог Future или Promise в других языках, представляющий вычисление, результат которого будет доступен в будущем.
await() — это функция-приостановка (suspend), которая вызывается на объекте Deferred и ожидает завершения соответствующей корутины, возвращая её результат.
Пример базового использования
import kotlinx.coroutines.*
suspend fun main() {
val time = measureTimeMillis {
val deferredResult1: Deferred<Int> = async { fetchData1() }
val deferredResult2: Deferred<Int> = async { fetchData2() }
// await() приостанавливает корутину, но не блокирует поток
val result1 = deferredResult1.await() // Ждём результат первой задачи
val result2 = deferredResult2.await() // Ждём результат второй задачи
println("Сумма: ${result1 + result2}")
}
println("Выполнено за $time мс")
}
suspend fun fetchData1(): Int {
delay(1000) // Имитация долгой операции
return 10
}
suspend fun fetchData2(): Int {
delay(1500) // Имитация долгой операции
return 20
}
Ключевые особенности работы
-
Параллельное выполнение
- Когда вы запускаете несколько
async-корутин, они выполняются конкурентно (параллельно, если позволяет диспетчер) - В примере выше
fetchData1()иfetchData2()запускаются практически одновременно
- Когда вы запускаете несколько
-
Ленивый запуск (lazy mode)
val deferred = async(start = CoroutineStart.LAZY) { // Этот код не запустится автоматически expensiveComputation() } // Запуск только при явном вызове start() или await() deferred.start() // или deferred.await() -
Структурированный параллелизм
coroutineScope { val deferred1 = async { task1() } val deferred2 = async { task2() } // Если здесь произойдёт исключение, ВСЕ дочерние корутины будут отменены val results = listOf(deferred1, deferred2).awaitAll() }
Отличия от launch
// launch - для "fire-and-forget" задач (без возвращаемого значения)
val job = launch {
// делаем что-то фоновое
}
// async - для задач с возвращаемым значением
val deferred = async {
// вычисляем значение
return@async 42
}
val result = deferred.await()
Обработка исключений
Критически важный момент: исключения в async-корутине НЕ пробрасываются автоматически в родительскую корутину до вызова await():
try {
val deferred = async {
throw RuntimeException("Ошибка в async!")
}
// Исключение произойдёт только здесь, при вызове await()
deferred.await()
} catch (e: Exception) {
println("Поймано: ${e.message}")
}
Для немедленной отмены родительской корутины при ошибке в async используйте SupervisorJob или supervisorScope.
Диспетчеры и async
// Запуск в IO-диспетчере для сетевых операций
val data = async(Dispatchers.IO) {
fetchFromNetwork()
}
// Запуск в Default для CPU-интенсивных задач
val result = async(Dispatchers.Default) {
computeHeavyAlgorithm()
}
Практические паттерны
-
Параллельная загрузка данных
suspend fun loadUserData(userId: String): UserData = coroutineScope { val profileDeferred = async { api.getProfile(userId) } val friendsDeferred = async { api.getFriends(userId) } val postsDeferred = async { api.getPosts(userId) } UserData( profile = profileDeferred.await(), friends = friendsDeferred.await(), posts = postsDeferred.await() ) } -
Ограничение параллелизма
val results = listOfUrls.map { url -> async { downloadFile(url) } }.awaitAll() // Все задачи запустятся параллельно
Производительность и предостережения
- Не создавайте чрезмерное количество
async-корутин для мелких задач — накладные расходы могут превысить выгоду - Всегда явно обрабатывайте исключения из
async-корутин - Используйте
awaitAll()для ожидания несколькихDeferred:val deferredList = listOf(async { task1() }, async { task2() }) val results = deferredList.awaitAll()
async/await в Kotlin предоставляет элегантный, типобезопасный способ работы с асинхронными вычислениями, сохраняя при этом структурированность кода и поддерживая отмену и обработку ошибок через механизмы структурированного параллелизма.