async: co to jest, Deferred i równoległe współprogramy — jak działają

Autor: IT Sectr Opublikowano: 2026-06-22 Czas czytania: 8 min

async — Coroutine Builder w Kotlinie, który uruchamia współprogram i zwraca obiekt Deferred<T> do uzyskania wyniku operacji asynchronicznej. async umożliwia równoległe wykonywanie wielu zadań i agregowanie ich wyników przez await(). Według przewodnika JetBrains, 2024, async jest preferowanym sposobem organizacji obliczeń równoległych w korutynach.

Najważniejsze

  • async — budowniczy współprogramów, zwracający Deferred<T> z wynikiem operacji asynchronicznej
  • Deferred — asynchroniczne przyrzeczenie, dziedziczące Job, z metodą await() do uzyskania wyniku
  • async umożliwia równoległe wykonywanie niezależnych zadań, skracając całkowity czas
  • await() — suspend-funkcja, wstrzymująca współprogram do uzyskania wyniku bez blokowania
  • Strukturalna współbieżność z async gwarantuje anulowanie wszystkich zadań równoległych przy błędzie

Czym jest async w Kotlin Coroutines?

async — to funkcja rozszerzenia CoroutineScope, tworząca współprogram z możliwością zwrócenia wyniku. W przeciwieństwie do launch, async zwraca Deferred<T> — obiekt reprezentujący przyszłą wartość. Współprogram uruchomiony przez async wykonuje się współbieżnie z innymi współprogramami, co umożliwia zrównoleglenie obliczeń.

async jest używany, gdy wynik operacji asynchronicznej jest potrzebny do dalszego przetwarzania: ładowanie danych z wielu źródeł, przetwarzanie wsadowe kolekcji, równoległe zapytania do różnych API. Deferred jest tworzony natychmiast, a wynik staje się dostępny po zakończeniu współprogramu.

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())
}

Składnia async i Deferred

Sygnatura async jest podobna do launch: te same parametry CoroutineContext, CoroutineStart i suspend-blok. Różnica polega na typie zwracanym — Deferred<T> zamiast Job, gdzie T jest typem wyniku zwróconego przez ostatnie wyrażenie bloku.

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

Deferred<T> — interfejs dziedziczący Job i dodający metody await(), getCompleted(), getCompletionExceptionOrNull(). await() jest suspend-funkcją i może być wywołany tylko wewnątrz współprogramu lub innej suspend-funkcji. getCompleted() — metoda nieblokująca, rzucająca wyjątkiem, jeśli współprogram jeszcze się nie zakończył.

Zadania równoległe z async

Główną zaletą async jest możliwość uruchamiania zadań równolegle. Zamiast sekwencyjnego oczekiwania na każdą operację (całkowity czas = suma czasów), async uruchamia współprogramy jednocześnie, skracając całkowity czas do czasu najdłuższej operacji.

Równoległe ładowanie danych

Typowy scenariusz — ładowanie profilu użytkownika, jego ustawień i historii zamówień trzema równoległymi zapytaniami async. Po zakończeniu wszystkich trzech wyniki są łączone w jeden model danych.

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()
    )
}

Funkcja coroutineScope uruchamia podrzędne współprogramy i oczekuje ich zakończenia przed zwróceniem wyniku. Cały blok wykonuje się równolegle, a nie sekwencyjnie.

Przetwarzanie wyników Deferred

Deferred obsługuje kilka metod pracy z wynikiem. Oprócz await() dostępne są metody sprawdzania statusu, pobierania gotowej wartości i agregacji wielu Deferred za pomocą funkcji narzędziowych Kotlin Coroutines.

MetodaOpisSuspend?
await()Wstrzymuje współprogram do uzyskania wynikuTak
getCompleted()Zwraca wynik, jeśli współprogram jest zakończony (w przeciwnym razie Exception)Nie
getCompletionExceptionOrNull()Zwraca wyjątek przy błędzie lub nullNie
isCompletedSprawdza, czy współprogram jest zakończonyNie

Do agregacji listy Deferred używana jest awaitAll() — funkcja wstrzymująca wykonanie do zakończenia wszystkich Deferred w kolekcji. Jeśli choć jeden zakończył się błędem, awaitAll() rzuca wyjątek.

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

Różnice między async a launch

Wybór między async a launch zależy od tego, czy potrzebny jest wynik wykonania współprogramu. launch zwraca Job i nadaje się do operacji fire-and-forget, async zwraca Deferred i jest używany do zadań zwracających dane. Pod względem składni i parametrów budownicze są identyczne.

  • launch → Job (brak wyniku), async → Deferred<T> (jest wynik T)
  • launch — zadania fire-and-forget (logowanie, buforowanie, wysyłanie analityki)
  • async — obliczenia równoległe (ładowanie z wielu API, przetwarzanie wsadowe)
  • Oba obsługują strukturalną współbieżność i parametry CoroutineStart
  • await() jest dostępny tylko dla Deferred, launch nie dostarcza wyniku

Ważna zasada: never use async for fire-and-forget. Jeśli wynik współprogramu nie jest potrzebny, użyj launch. async tworzy narzut na tworzenie Deferred, który w takim scenariuszu nie jest uzasadniony.

Przykłady async w programowaniu mobilnym

W Androidzie async jest używany do operacji równoległych wewnątrz ViewModel i UseCases. Typowe scenariusze: jednoczesne ładowanie profilu i kanału aktualności, pobieranie pogody z kilku serwisów meteorologicznych, wsadowa synchronizacja danych z serwerem.

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 }
        }
    }
}

Zaleca się opakowywanie grupy async w coroutineScope lub supervisorScope w celu izolacji błędów. supervisorScope umożliwia kontynuację wykonywania pozostałych współprogramów async nawet przy błędzie w jednym z nich — przydatne w przypadku niekrytycznych operacji.

Często zadawane pytania

Czy można używać async bez await?

Można, ale to bez sensu. async bez await() uruchamia współprogram, ale jego wynik jest tracony. Jeśli wynik nie jest potrzebny, użyj launch — nie tworzy on Deferred i jest wydajniejszy pod względem pamięci i wydajności.

Jak async obsługuje wyjątki?

Wyjątek wewnątrz async jest zapisywany w Deferred. Wywołanie await() przerzuca ten wyjątek. Do sprawdzenia bez przerzucania użyj getCompletionExceptionOrNull(). Przy użyciu coroutineScope błąd anuluje wszystkie podrzędne współprogramy.

Czym jest awaitAll w Kotlin Coroutines?

awaitAll() — funkcja rozszerzenia dla Iterable<Deferred<T>>, wstrzymująca współprogram do zakończenia wszystkich Deferred. Zwraca listę wyników. W przypadku błędu choć jednego Deferred rzuca wyjątek i anuluje pozostałe.

Czy async różni się od GlobalScope.async?

Tak. async — funkcja rozszerzenia CoroutineScope, związana z zakresem. GlobalScope.async tworzy współprogram bez powiązania z cyklem życia — może wykonywać się w nieskończoność. W Androidzie GlobalScope nie jest zalecany ze względu na ryzyko wycieków.

Ile współprogramów async można uruchomić dla równoległych zapytań?

Liczba jest podyktowana dyspozytorem: Dispatchers.IO obsługuje do 64 równoczesnych wątków, Dispatchers.Default — według liczby rdzeni CPU. Dla operacji masowych używaj ograniczonego równoległości przez Semaphore lub mapNotNull z awaitAll.

Podsumowanie

  • async — budowniczy współprogramów, zwracający Deferred<T> do asynchronicznego uzyskania wyniku
  • Deferred dziedziczy Job i dodaje metody await(), getCompleted(), getCompletionExceptionOrNull()
  • Wykonanie równoległe z async skraca całkowity czas do czasu najdłuższej operacji
  • awaitAll() — narzędzie do oczekiwania na wszystkie Deferred w kolekcji
  • coroutineScope — zalecane opakowanie dla grupy async z gwarancją anulowania
  • Nie używaj async dla fire-and-forget — stosuj launch
  • W Androidzie async jest używany do równoległego ładowania w ViewModel z viewModelScope

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również