async — Coroutine Builder Kotlinban, amely elindít egy korutint és egy Deferred<T> objektumot ad vissza az aszinkron művelet eredményének lekéréséhez. Az async lehetővé teszi több feladat párhuzamos végrehajtását és azok eredményeinek összesítését az await()-en keresztül. A JetBrains, 2024 útmutatója szerint az async a preferált módszer a párhuzamos számítások szervezésére korutinokban.
Főbb pontok
async — egy CoroutineScope kiterjesztő függvény, amely korutint hoz létre eredmény-visszaadással. A launch-tól eltérően az async Deferred<T>-t ad vissza — egy objektumot, amely egy jövőbeli értéket képvisel. Az async segítségével elindított korutin más korutinokkal konkurensen hajtódik végre, lehetővé téve a számítások párhuzamosítását.
Az async akkor használatos, amikor az aszinkron művelet eredménye szükséges a további feldolgozáshoz: adatok betöltése több forrásból, gyűjtemények kötegelt feldolgozása, párhuzamos kérések különböző API-khoz. Deferred azonnal létrejön, és az eredmény a korutin befejezése után válik elérhetővé.
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())
}
Az async aláírása hasonló a launch-hoz: ugyanazok a CoroutineContext, CoroutineStart paraméterek és szuszpendáló blokk. A különbség a visszaadott típusban van — Deferred<T> a Job helyett, ahol T a blokk utolsó kifejezése által visszaadott eredmény típusa.
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
Deferred<T> — interfész, amely örökli a Job-ot és hozzáadja az await(), getCompleted(), getCompletionExceptionOrNull() metódusokat. Az await() szuszpendáló függvény, és csak korutinon belül vagy más szuszpendáló függvényben hívható meg. A getCompleted() — nem blokkoló metódus, amely kivételt dob, ha a korutin még nem fejeződött be.
Az async fő előnye a feladatok párhuzamos indításának lehetősége. A szekvenciális várakozás helyett (teljes idő = idők összege) az async egyidejűleg indítja a korutinokat, csökkentve a teljes időt a leghosszabb művelet idejére.
Tipikus forgatókönyv — a felhasználói profil, beállítások és rendelési előzmények betöltése három párhuzamos async kéréssel. Mindhárom befejezése után az eredmények egyetlen adatmodellbe egyesülnek.
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()
)
}
A coroutineScope függvény elindítja a gyermek korutinokat, és megvárja a befejezésüket, mielőtt visszaadja az eredményt. Az egész blokk párhuzamosan hajtódik végre, nem szekvenciálisan.
A Deferred több metódust támogat az eredménnyel való munkához. Az await() mellett elérhetők metódusok az állapot ellenőrzésére, a kész érték lekérésére és több Deferred aggregálására a Kotlin Coroutines segédfüggvényein keresztül.
| Metódus | Leírás | Suspend? |
|---|---|---|
| await() | Felfüggeszti a korutint az eredmény beérkezéséig | Igen |
| getCompleted() | Visszaadja az eredményt, ha a korutin befejeződött (egyébként Exception) | Nem |
| getCompletionExceptionOrNull() | Hiba esetén visszaadja a kivételt vagy null-t | Nem |
| isCompleted | Ellenőrzi, hogy a korutin befejeződött-e | Nem |
Deferred lista aggregálásához az awaitAll() használatos — egy függvény, amely felfüggeszti a végrehajtást, amíg a gyűjtemény összes Deferred-je be nem fejeződik. Ha legalább egy hiba miatt fejeződött be, az awaitAll() kivételt dob.
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
return coroutineScope {
val deferreds = requests.map { async { it() } }
deferreds.awaitAll()
}
}
Az async és launch közötti választás attól függ, hogy szükség van-e a korutin végrehajtásának eredményére. A launch Job-ot ad vissza, és fire-and-forget műveletekhez alkalmas, az async Deferred-et ad vissza, és adatokat visszaadó feladatokhoz használatos. Szintaxis és paraméterek tekintetében az építők azonosak.
Fontos szabály: soha ne használjuk az async-ot fire-and-forget-re. Ha a korutin eredménye nem szükséges, használjuk a launch-ot. Az async többletterhelést jelent a Deferred létrehozásában, ami ilyen forgatókönyvben nem indokolt.
Androidban az async párhuzamos műveletekhez használatos a ViewModel-en és UseCases-eken belül. Tipikus forgatókönyvek: profil és hírfolyam egyidejű betöltése, időjárás lekérése több meteorológiai szolgáltatástól, adatok kötegelt szinkronizálása a szerverrel.
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 }
}
}
}
Javasolt az async csoportot coroutineScope vagy supervisorScope csomagolni a hibák elkülönítéséhez. A supervisorScope lehetővé teszi a többi async korutin végrehajtásának folytatását még akkor is, ha az egyikben hiba történik — hasznos nem kritikus műveleteknél.
Gyakran Ismételt Kérdések
Használható, de értelmetlen. Az async await() nélkül elindítja a korutint, de az eredménye elveszik. Ha az eredmény nem szükséges, használjuk a launch-ot — az nem hoz létre Deferred-et, és hatékonyabb memória és teljesítmény szempontjából.
Az async belsejében lévő kivétel a Deferred-ben tárolódik. Az await() hívás továbbítja ezt a kivételt. A továbbítás nélküli ellenőrzéshez használjuk a getCompletionExceptionOrNull()-t. A coroutineScope használatakor a hiba megszakítja az összes gyermek korutint.
awaitAll() — kiterjesztő függvény az Iterable<Deferred<T>> számára, amely felfüggeszti a korutint, amíg az összes Deferred be nem fejeződik. Visszaadja az eredmények listáját. Ha legalább egy Deferred hiba miatt fejeződik be, továbbítja a kivételt és megszakítja a többit.
Igen. Az async a CoroutineScope kiterjesztő függvénye, a scope-hoz kötve. A GlobalScope.async korutint hoz létre az életciklushoz való kötődés nélkül — végtelenül futhat. Androidban a GlobalScope nem ajánlott a memóriaszivárgás kockázata miatt.
A számot az ütemező határozza meg: a Dispatchers.IO akár 64 egyidejű szálat támogat, a Dispatchers.Default — a CPU magok számának megfelelően. Tömeges műveletekhez használjunk korlátozott párhuzamosságot Semaphore vagy mapNotNull segítségével awaitAll-lel.
Összefoglaló
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is