async — Coroutine Builder i Kotlin som startar en korutin och returnerar ett Deferred<T>-objekt för att få resultatet av en asynkron operation. async gör det möjligt att utföra flera uppgifter parallellt och aggregera deras resultat via await(). Enligt JetBrains guide, 2024 är async det föredragna sättet att organisera parallella beräkningar i korutiner.
Huvudpunkter
async — är en utökningsfunktion för CoroutineScope som skapar en korutin med resultatåtergivning. Till skillnad från launch returnerar async Deferred<T> — ett objekt som representerar ett framtida värde. Korutinen som startas via async exekveras samtidigt med andra korutiner, vilket möjliggör parallellisering av beräkningar.
async används när resultatet av en asynkron operation behövs för vidare bearbetning: laddning av data från flera källor, batchbearbetning av samlingar, parallella förfrågningar till olika API:er. Deferred skapas omedelbart och resultatet blir tillgängligt efter att korutinen slutförts.
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())
}
Signaturen för async liknar launch: samma parametrar CoroutineContext, CoroutineStart och suspend-block. Skillnaden ligger i returtypen — Deferred<T> istället för Job, där T är typen av resultatet som returneras av blockets sista uttryck.
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
Deferred<T> — gränssnitt som ärver Job och lägger till metoderna await(), getCompleted(), getCompletionExceptionOrNull(). await() är en suspend-funktion och kan endast anropas inuti en korutin eller en annan suspend-funktion. getCompleted() — en icke-blockerande metod som kastar ett undantag om korutinen inte har slutförts ännu.
Den främsta fördelen med async är möjligheten att starta uppgifter parallellt. Istället för sekventiell väntan på varje operation (total tid = summan av tider) startar async korutiner samtidigt, vilket minskar den totala tiden till tiden för den längsta operationen.
Ett typiskt scenario — laddning av användarprofil, inställningar och orderhistorik med tre parallella async-förfrågningar. Efter att alla tre slutförts kombineras resultaten till en enda datamodell.
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()
)
}
Funktionen coroutineScope startar underordnade korutiner och väntar på deras slutförande innan resultatet returneras. Hela blocket exekveras parallellt, inte sekventiellt.
Deferred stöder flera metoder för att arbeta med resultatet. Förutom await() finns metoder för statuskontroll, hämtning av färdigt värde och aggregering av flera Deferred via verktygsfunktioner i Kotlin Coroutines.
| Metod | Beskrivning | Suspend? |
|---|---|---|
| await() | Pausar korutinen tills resultatet erhålls | Ja |
| getCompleted() | Returnerar resultatet om korutinen har slutförts (annars Exception) | Nej |
| getCompletionExceptionOrNull() | Returnerar undantaget vid fel eller null | Nej |
| isCompleted | Kontrollerar om korutinen har slutförts | Nej |
För aggregering av en lista med Deferred används awaitAll() — en funktion som pausar exekveringen tills alla Deferred i samlingen har slutförts. Om minst en har slutförts med fel kastar awaitAll() ett undantag.
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
return coroutineScope {
val deferreds = requests.map { async { it() } }
deferreds.awaitAll()
}
}
Valet mellan async och launch beror på om resultatet av korutinens exekvering behövs. launch returnerar Job och är lämplig för fire-and-forget-operationer, async returnerar Deferred och används för uppgifter som returnerar data. När det gäller syntax och parametrar är byggarna identiska.
Viktig regel: använd aldrig async för fire-and-forget. Om resultatet av korutinen inte behövs, använd launch. async skapar overhead för att skapa Deferred, vilket inte är motiverat i ett sådant scenario.
I Android används async för parallella operationer i ViewModel och UseCases. Typiska scenarier: samtidig laddning av profil och nyhetsflöde, hämtning av väder från flera väder tjänster, batchesynkronisering av data med servern.
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 }
}
}
}
Det rekommenderas att slå in async-gruppen i coroutineScope eller supervisorScope för feltisolering. supervisorScope gör det möjligt att fortsätta exekveringen av de återstående async-korutinerna även om ett fel uppstår i en av dem — användbart för icke-kritiska operationer.
Vanliga frågor
Det kan man, men det är meningslöst. async utan await() startar korutinen, men dess resultat går förlorat. Om resultatet inte behövs, använd launch — det skapar inget Deferred och är mer effektivt när det gäller minne och prestanda.
Undantaget inuti async lagras i Deferred. Anropet await() vidarebefordrar detta undantag. För kontroll utan vidarebefordran, använd getCompletionExceptionOrNull(). Vid användning av coroutineScope avbryter felet alla underordnade korutiner.
awaitAll() — en utökningsfunktion för Iterable<Deferred<T>> som pausar korutinen tills alla Deferred har slutförts. Returnerar en lista med resultat. Om minst en Deferred slutförs med fel vidarebefordrar den undantaget och avbryter resten.
Ja. async är en utökningsfunktion för CoroutineScope, bunden till scopet. GlobalScope.async skapar en korutin utan bindning till livscykeln — den kan köras oändligt. I Android rekommenderas inte GlobalScope på grund av risk för minnesläckor.
Antalet bestäms av dispatchern: Dispatchers.IO stöder upp till 64 samtidiga trådar, Dispatchers.Default — baserat på antalet CPU-kärnor. För massiva operationer, använd begränsad parallellitet via Semaphore eller mapNotNull med awaitAll.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också