async — Coroutine Builder v Kotlinu, který spouští koroutinu a vrací objekt Deferred<T> pro získání výsledku asynchronní operace. async umožňuje paralelní provádění více úkolů a agregaci jejich výsledků pomocí await(). Podle průvodce JetBrains, 2024 je async preferovaným způsobem organizace paralelních výpočtů v koroutinách.
Hlavní body
async — je rozšiřující funkce CoroutineScope, která vytváří koroutinu s vracením výsledku. Na rozdíl od launch, async vrací Deferred<T> — objekt představující budoucí hodnotu. Koroutina spuštěná přes async se provádí souběžně s ostatními koroutinami, což umožňuje paralelizaci výpočtů.
async se používá, když je výsledek asynchronní operace potřebný pro další zpracování: načítání dat z více zdrojů, dávkové zpracování kolekcí, paralelní požadavky na různá API. Deferred je vytvořen okamžitě a výsledek je k dispozici po dokončení koroutiny.
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())
}
Signatura async je podobná launch: stejné parametry CoroutineContext, CoroutineStart a suspend-blok. Rozdíl je v návratovém typu — Deferred<T> místo Job, kde T je typ výsledku vráceného posledním výrazem bloku.
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
Deferred<T> — rozhraní, které dědí Job a přidává metody await(), getCompleted(), getCompletionExceptionOrNull(). await() je suspend-funkce a může být volána pouze uvnitř koroutiny nebo jiné suspend-funkce. getCompleted() — neblokující metoda, která vyvolá výjimku, pokud koroutina ještě nebyla dokončena.
Hlavní výhodou async je možnost spouštět úkoly paralelně. Místo sekvenčního čekání na každou operaci (celkový čas = součet časů) async spouští koroutiny současně, čímž zkracuje celkový čas na čas nejdelší operace.
Typický scénář — načítání profilu uživatele, jeho nastavení a historie objednávek třemi paralelními async požadavky. Po dokončení všech tří jsou výsledky sloučeny do jediného datového modelu.
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()
)
}
Funkce coroutineScope spouští podřízené koroutiny a čeká na jejich dokončení před vrácením výsledku. Celý blok se provádí paralelně, ne sekvenčně.
Deferred podporuje několik metod pro práci s výsledkem. Kromě await() jsou k dispozici metody pro kontrolu stavu, získání hotové hodnoty a agregaci více Deferred pomocí pomocných funkcí Kotlin Coroutines.
| Metoda | Popis | Suspend? |
|---|---|---|
| await() | Pozastaví koroutinu do získání výsledku | Ano |
| getCompleted() | Vrátí výsledek, pokud je koroutina dokončena (jinak Exception) | Ne |
| getCompletionExceptionOrNull() | Vrátí výjimku při chybě nebo null | Ne |
| isCompleted | Zkontroluje, zda je koroutina dokončena | Ne |
Pro agregaci seznamu Deferred se používá awaitAll() — funkce, která pozastaví provádění, dokud nejsou dokončeny všechny Deferred v kolekci. Pokud alespoň jeden skončil chybou, awaitAll() vyvolá výjimku.
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
return coroutineScope {
val deferreds = requests.map { async { it() } }
deferreds.awaitAll()
}
}
Výběr mezi async a launch závisí na tom, zda je potřeba výsledek provádění koroutiny. launch vrací Job a je vhodný pro operace fire-and-forget, async vrací Deferred a používá se pro úkoly vracející data. Z hlediska syntaxe a parametrů jsou stavitelé identičtí.
Důležité pravidlo: nikdy nepoužívejte async pro fire-and-forget. Pokud výsledek koroutiny není potřeba, použijte launch. async vytváří režii na vytvoření Deferred, která není v takovém scénáři ospravedlnitelná.
V Androidu se async používá pro paralelní operace uvnitř ViewModel a UseCases. Typické scénáře: současné načítání profilu a zpravodajského kanálu, získávání počasí z více meteorologických služeb, dávková synchronizace dat se serverem.
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 }
}
}
}
Doporučuje se zabalit skupinu async do coroutineScope nebo supervisorScope pro izolaci chyb. supervisorScope umožňuje pokračovat v provádění ostatních async koroutin i při chybě v jedné z nich — užitečné pro nekritické operace.
Často kladené otázky
Lze, ale je to zbytečné. async bez await() spouští koroutinu, ale její výsledek se ztrácí. Pokud výsledek není potřeba, použijte launch — nevytváří Deferred a je efektivnější z hlediska paměti a výkonu.
Výjimka uvnitř async je uložena v Deferred. Volání await() tuto výjimku propaguje. Pro kontrolu bez propagace použijte getCompletionExceptionOrNull(). Při použití coroutineScope chyba zruší všechny podřízené koroutiny.
awaitAll() — rozšiřující funkce pro Iterable<Deferred<T>>, která pozastaví koroutinu, dokud nejsou dokončeny všechny Deferred. Vrací seznam výsledků. Pokud alespoň jeden Deferred skončí chybou, propaguje výjimku a zruší ostatní.
Ano. async je rozšiřující funkce CoroutineScope, vázaná na scope. GlobalScope.async vytváří koroutinu bez vazby na životní cyklus — může běžet donekonečna. V Androidu se GlobalScope nedoporučuje kvůli riziku úniků paměti.
Počet je dán dispečerem: Dispatchers.IO podporuje až 64 současných vláken, Dispatchers.Default — podle počtu jader CPU. Pro hromadné operace používejte omezený paralelismus přes Semaphore nebo mapNotNull s awaitAll.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také