async — Coroutine Builder in Kotlin die een coroutine start en een Deferred<T>-object retourneert om het resultaat van een asynchrone bewerking te verkrijgen. async maakt het mogelijk om meerdere taken parallel uit te voeren en hun resultaten te aggregeren via await(). Volgens de JetBrains-gids, 2024, is async de voorkeursmethode voor het organiseren van parallelle berekeningen in coroutines.
Belangrijkste
async — is een extensiefunctie van CoroutineScope die een coroutine met resultaatretour creëert. In tegenstelling tot launch retourneert async Deferred<T> — een object dat een toekomstige waarde vertegenwoordigt. De via async gestarte coroutine wordt concurrent met andere coroutines uitgevoerd, wat parallellisatie van berekeningen mogelijk maakt.
async wordt gebruikt wanneer het resultaat van een asynchrone bewerking nodig is voor verdere verwerking: het laden van gegevens uit meerdere bronnen, batchverwerking van collecties, parallelle aanvragen naar verschillende API's. Deferred wordt onmiddellijk aangemaakt en het resultaat wordt beschikbaar na voltooiing van de coroutine.
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())
}
De handtekening van async is vergelijkbaar met launch: dezelfde parameters CoroutineContext, CoroutineStart en suspend-blok. Het verschil zit in het retourtype — Deferred<T> in plaats van Job, waarbij T het type is van het resultaat dat door de laatste expressie van het blok wordt geretourneerd.
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
Deferred<T> — interface die Job erft en de methoden await(), getCompleted(), getCompletionExceptionOrNull() toevoegt. await() is een suspend-functie en kan alleen binnen een coroutine of een andere suspend-functie worden aangeroepen. getCompleted() — een niet-blokkerende methode die een uitzondering gooit als de coroutine nog niet is voltooid.
Het belangrijkste voordeel van async is de mogelijkheid om taken parallel te starten. In plaats van sequentieel wachten op elke bewerking (totale tijd = som van tijden), start async coroutines gelijktijdig, waardoor de totale tijd wordt teruggebracht tot de tijd van de langste bewerking.
Een typisch scenario — het laden van het gebruikersprofiel, de instellingen en de bestelgeschiedenis met drie parallelle async-verzoeken. Na voltooiing van alle drie worden de resultaten samengevoegd in één gegevensmodel.
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()
)
}
De functie coroutineScope start onderliggende coroutines en wacht op hun voltooiing voordat het resultaat wordt geretourneerd. Het hele blok wordt parallel uitgevoerd, niet sequentieel.
Deferred ondersteunt verschillende methoden voor het werken met het resultaat. Naast await() zijn er methoden voor statuscontrole, het ophalen van de gereed zijnde waarde en aggregatie van meerdere Deferred via de hulpfuncties van Kotlin Coroutines.
| Methode | Beschrijving | Suspend? |
|---|---|---|
| await() | Onderbreekt de coroutine totdat het resultaat is verkregen | Ja |
| getCompleted() | Retourneert het resultaat als de coroutine is voltooid (anders Exception) | Nee |
| getCompletionExceptionOrNull() | Retourneert de uitzondering bij een fout of null | Nee |
| isCompleted | Controleert of de coroutine is voltooid | Nee |
Voor aggregatie van een lijst met Deferred wordt awaitAll() gebruikt — een functie die de uitvoering onderbreekt totdat alle Deferred in de collectie zijn voltooid. Als er minstens één met een fout is geëindigd, gooit awaitAll() een uitzondering.
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
return coroutineScope {
val deferreds = requests.map { async { it() } }
deferreds.awaitAll()
}
}
De keuze tussen async en launch hangt af van of het resultaat van de coroutine-uitvoering nodig is. launch retourneert Job en is geschikt voor fire-and-forget-bewerkingen, async retourneert Deferred en wordt gebruikt voor taken die gegevens retourneren. Qua syntaxis en parameters zijn de bouwers identiek.
Belangrijke regel: gebruik async nooit voor fire-and-forget. Als het resultaat van de coroutine niet nodig is, gebruik dan launch. async creëert overhead voor het maken van Deferred, wat in een dergelijk scenario niet gerechtvaardigd is.
In Android wordt async gebruikt voor parallelle bewerkingen binnen ViewModel en UseCases. Typische scenario's: gelijktijdig laden van profiel en nieuwsfeed, ophalen van weer van meerdere weerdiensten, batch-synchronisatie van gegevens met de server.
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 }
}
}
}
Het wordt aanbevolen om een groep async in coroutineScope of supervisorScope te wrappen voor foutisolatie. supervisorScope maakt het mogelijk om de uitvoering van de overige async-coroutines voort te zetten, zelfs als er een fout optreedt in één ervan — nuttig voor niet-kritieke bewerkingen.
Veelgestelde vragen
Het kan, maar het is zinloos. async zonder await() start de coroutine, maar het resultaat gaat verloren. Als het resultaat niet nodig is, gebruik dan launch — het maakt geen Deferred aan en is efficiënter qua geheugen en prestaties.
De uitzondering binnen async wordt opgeslagen in Deferred. De aanroep await() gooit deze uitzondering door. Gebruik getCompletionExceptionOrNull() om te controleren zonder door te gooien. Bij gebruik van coroutineScope annuleert de fout alle onderliggende coroutines.
awaitAll() — een extensiefunctie voor Iterable<Deferred<T>> die de coroutine onderbreekt totdat alle Deferred zijn voltooid. Retourneert een lijst met resultaten. Als ten minste één Deferred met een fout eindigt, gooit het de uitzondering door en annuleert de rest.
Ja. async is een extensiefunctie van CoroutineScope, gebonden aan de scope. GlobalScope.async maakt een coroutine zonder binding aan de levenscyclus — deze kan oneindig worden uitgevoerd. In Android wordt GlobalScope afgeraden vanwege het risico op geheugenlekken.
Het aantal wordt bepaald door de dispatcher: Dispatchers.IO ondersteunt tot 64 gelijktijdige threads, Dispatchers.Default — op basis van het aantal CPU-kernen. Gebruik voor massale bewerkingen beperkte parallelliteit via Semaphore of mapNotNull met awaitAll.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook