async: что это такое, Deferred и параллельные сопрограммы как работают

Автор: IT Sectr Опубликовано: 2026-06-22 Время чтения: 8 мин

async — Coroutine Builder в Kotlin, который запускает сопрограмму и возвращает объект Deferred<T> для получения результата асинхронной операции. async позволяет выполнять несколько задач параллельно и агрегировать их результаты через await(). По данным руководства JetBrains, 2024, async является предпочтительным способом организации параллельных вычислений в корутинах.

Главное

  • async — билдер сопрограмм, возвращающий Deferred<T> с результатом асинхронной операции
  • Deferred — асинхронное обещание, наследующее Job, с методом await() для получения результата
  • async позволяет выполнять независимые задачи параллельно, сокращая общее время
  • await() — suspend-функция, приостанавливающая корутину до получения результата без блокировки
  • Структурированная конкурентность с async гарантирует отмену всех параллельных задач при ошибке

Что такое async в Kotlin Coroutines?

async — это функция-расширение CoroutineScope, создающая сопрограмму с возвратом результата. В отличие от launch, async возвращает Deferred<T> — объект, который представляет будущее значение. Корутина, запущенная через async, выполняется конкурентно с другими корутинами, что позволяет распараллеливать вычисления.

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

kotlin
import kotlinx.coroutines.*

suspend fun String.delayedValue(): String {
    delay(1000L)
    return this
}

fun main() = runBlocking {
    val deferred: Deferred<String> = async { "Hello".delayedValue() }
    println(deferred.await())
}

Синтаксис async и Deferred

Сигнатура async аналогична launch: те же параметры CoroutineContext, CoroutineStart и suspend-блок. Различие в возвращаемом типе — Deferred<T> вместо Job, где T — тип результата, возвращаемого последним выражением блока.

kotlin
public fun <T> CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
): Deferred<T>

Deferred<T> — интерфейс, наследующий Job и добавляющий методы await(), getCompleted(), getCompletionExceptionOrNull(). await() является suspend-функцией и может быть вызван только внутри корутины или другой suspend-функции. getCompleted() — неблокирующий метод, выбрасывающий исключение, если корутина ещё не завершена.

Параллельные задачи с async

Основное преимущество async — возможность запускать задачи параллельно. Вместо последовательного ожидания каждой операции (общее время = сумма времён), async запускает корутины одновременно, сокращая общее время до времени самой долгой операции.

Параллельная загрузка данных

Типовой сценарий — загрузка профиля пользователя, его настроек и истории заказов тремя параллельными async-запросами. После завершения всех трёх результаты объединяются в единую модель данных.

kotlin
suspend fun loadUserDashboard(userId: String): Dashboard = coroutineScope {
    val profile = async { api.getProfile(userId) }
    val settings = async { api.getSettings(userId) }
    val orders = async { api.getRecentOrders(userId) }

    Dashboard(
        profile = profile.await(),
        settings = settings.await(),
        orders = orders.await()
    )
}

Функция coroutineScope запускает дочерние корутины и ожидает их завершения перед возвратом результата. При этом весь блок выполняется параллельно, а не последовательно.

Обработка результатов Deferred

Deferred поддерживает несколько методов для работы с результатом. Помимо await(), доступны методы для проверки статуса, получения готового значения и агрегации нескольких Deferred через функции-утилиты Kotlin Coroutines.

МетодОписаниеSuspend?
await()Приостанавливает корутину до получения результатаДа
getCompleted()Возвращает результат, если корутина завершена (инасе Exception)Нет
getCompletionExceptionOrNull()Возвращает исключение при ошибке или nullНет
isCompletedПроверяет, завершена ли корутинаНет

Для агрегации списка Deferred используется awaitAll() — функция, приостанавливающая выполнение до завершения всех Deferred в коллекции. Если хотя бы один завершился с ошибкой, awaitAll() пробрасывает исключение.

kotlin
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
    return coroutineScope {
        val deferreds = requests.map { async { it() } }
        deferreds.awaitAll()
    }
}

Отличия async от launch

Выбор между async и launch зависит от того, нужен ли результат выполнения корутины. launch возвращает Job и подходит для операций fire-and-forget, async возвращает Deferred и используется для задач, возвращающих данные. По синтаксису и параметрам билдеры идентичны.

  • launch → Job (нет результата), async → Deferred<T> (есть результат T)
  • launch — fire-and-forget задачи (логирование, кэширование, отправка аналитики)
  • async — параллельные вычисления (загрузка из нескольких API, пакетная обработка)
  • Оба поддерживают структурированную конкурентность и параметры CoroutineStart
  • await() доступен только для Deferred, launch не предоставляет результата

Важное правило: never use async for fire-and-forget. Если результат корутины не нужен, используйте launch. async создаёт накладные расходы на создание Deferred, которые в таком сценарии не оправданы.

Примеры async в мобильной разработке

В Android async применяется для параллельных операций внутри ViewModel и UseCases. Типовые сценарии: загрузка профиля и ленты новостей одновременно, получение погоды с нескольких метеосервисов, пакетная синхронизация данных с сервером.

kotlin
class HomeViewModel : ViewModel() {
    fun loadHomeScreen() {
        viewModelScope.launch {
            val userData = async(Dispatchers.IO) { repository.getUserData() }
            val newsFeed = async(Dispatchers.IO) { repository.getNewsFeed() }
            val notifications = async(Dispatchers.IO) { repository.getNotifications() }

            val state = HomeState(
                user = userData.await(),
                news = newsFeed.await(),
                unread = notifications.await()
            )
            _uiState.update { state }
        }
    }
}

Рекомендуется оборачивать группу async в coroutineScope или supervisorScope для изоляции ошибок. supervisorScope позволяет продолжить выполнение остальных async-корутин даже при ошибке в одной из них — полезно для некритичных операций.

Часто задаваемые вопросы

Можно ли использовать async без await?

Можно, но бессмысленно. async без await() запускает корутину, но её результат теряется. Если результат не нужен, используйте launch — он не создаёт Deferred и эффективнее по памяти и производительности.

Как async обрабатывает исключения?

Исключение внутри async сохраняется в Deferred. Вызов await() пробрасывает это исключение. Для проверки без проброса используйте getCompletionExceptionOrNull(). При использовании coroutineScope ошибка отменяет все дочерние корутины.

Что такое awaitAll в Kotlin Coroutines?

awaitAll() — функция-расширение для Iterable<Deferred<T>>, приостанавливающая корутину до завершения всех Deferred. Возвращает список результатов. При ошибке хотя бы одного Deferred пробрасывает исключение и отменяет остальные.

Отличается ли async от GlobalScope.async?

Да. async — функция-расширение CoroutineScope, привязанная к scope. GlobalScope.async создаёт корутину без привязки к жизненному циклу — она может выполняться бесконечно. В Android GlobalScope не рекомендуется из-за риска утечек.

Сколько async корутин можно запустить для параллельных запросов?

Количество диктуется диспетчером: Dispatchers.IO поддерживает до 64 одновременных потоков, Dispatchers.Default — по числу CPU-ядер. Для массовых операций используйте ограниченный параллелизм через Semaphore или mapNotNull с awaitAll.

Итоги

  • async — билдер сопрограмм, возвращающий Deferred<T> для асинхронного получения результата
  • Deferred наследует Job и добавляет методы await(), getCompleted(), getCompletionExceptionOrNull()
  • Параллельное выполнение с async сокращает общее время до длительности самой долгой операции
  • awaitAll() — утилита для ожидания всех Deferred в коллекции
  • coroutineScope — рекомендуемая обёртка для группы async с гарантией отмены
  • Не используйте async для fire-and-forget — применяйте launch
  • В Android async применяется для параллельных загрузок в ViewModel с viewModelScope

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также