async — ist ein Coroutine Builder in Kotlin, der eine Coroutine startet und ein Deferred<T>-Objekt zurückgibt, um das Ergebnis einer asynchronen Operation zu erhalten. async ermöglicht die parallele Ausführung mehrerer Aufgaben und die Aggregation ihrer Ergebnisse über await(). Laut JetBrains-Leitfaden, 2024 ist async die bevorzugte Methode zur Organisation paralleler Berechnungen in Coroutinen.
Wichtigste Erkenntnisse
async — ist eine CoroutineScope-Erweiterungsfunktion, die eine Coroutine erstellt, die ein Ergebnis zurückgibt. Im Gegensatz zu launch gibt async Deferred<T> zurück — ein Objekt, das einen zukünftigen Wert repräsentiert. Eine über async gestartete Coroutine wird gleichzeitig mit anderen Coroutinen ausgeführt, was die Parallelisierung von Berechnungen ermöglicht.
async wird verwendet, wenn das Ergebnis einer asynchronen Operation für die weitere Verarbeitung benötigt wird: Laden von Daten aus mehreren Quellen, Stapelverarbeitung von Sammlungen, parallele Anfragen an verschiedene APIs. Deferred wird sofort erstellt, und das Ergebnis wird nach Abschluss der Coroutine verfügbar.
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())
}
Die Signatur von async ähnelt launch: dieselben Parameter CoroutineContext, CoroutineStart und ein Suspend-Block. Der Unterschied liegt im Rückgabetyp — Deferred<T> statt Job, wobei T der Ergebnistyp ist, der vom letzten Ausdruck des Blocks zurückgegeben wird.
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
Deferred<T> — ein Interface, das Job erbt und die Methoden await(), getCompleted(), getCompletionExceptionOrNull() hinzufügt. await() ist eine Suspend-Funktion und kann nur innerhalb einer Coroutine oder einer anderen Suspend-Funktion aufgerufen werden. getCompleted() ist eine nicht blockierende Methode, die eine Ausnahme auslöst, wenn die Coroutine noch nicht abgeschlossen ist.
Der Hauptvorteil von async ist die Möglichkeit, Aufgaben parallel auszuführen. Anstatt auf jede Operation nacheinander zu warten (Gesamtzeit = Summe der Zeiten), startet async Coroutinen gleichzeitig und reduziert die Gesamtzeit auf die Dauer der längsten Operation.
Ein typisches Szenario — Laden des Benutzerprofils, seiner Einstellungen und des Bestellverlaufs mit drei parallelen async-Anfragen. Nach Abschluss aller drei werden die Ergebnisse zu einem einzigen Datenmodell zusammengeführt.
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()
)
}
Die Funktion coroutineScope startet untergeordnete Coroutinen und wartet auf deren Abschluss, bevor sie das Ergebnis zurückgibt. Der gesamte Block wird parallel ausgeführt, nicht sequenziell.
Deferred unterstützt mehrere Methoden zur Arbeit mit dem Ergebnis. Neben await() gibt es Methoden zum Überprüfen des Status, zum Abrufen eines abgeschlossenen Werts und zum Aggregieren mehrerer Deferreds über die Hilfsfunktionen von Kotlin Coroutines.
| Methode | Beschreibung | Suspend? |
|---|---|---|
| await() | Hält die Coroutine an, bis das Ergebnis vorliegt | Ja |
| getCompleted() | Gibt das Ergebnis zurück, wenn die Coroutine abgeschlossen ist (sonst Exception) | Nein |
| getCompletionExceptionOrNull() | Gibt die Ausnahme bei Fehler oder null zurück | Nein |
| isCompleted | Überprüft, ob die Coroutine abgeschlossen ist | Nein |
Zur Aggregation einer Liste von Deferreds wird awaitAll() verwendet — eine Funktion, die die Ausführung anhält, bis alle Deferreds in der Sammlung abgeschlossen sind. Wenn mindestens einer fehlerhaft abgeschlossen wurde, löst awaitAll() die Ausnahme aus.
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
return coroutineScope {
val deferreds = requests.map { async { it() } }
deferreds.awaitAll()
}
}
Die Wahl zwischen async und launch hängt davon ab, ob das Ergebnis der Coroutine benötigt wird. launch gibt Job zurück und eignet sich für Fire-and-Forget-Operationen, async gibt Deferred zurück und wird für Aufgaben verwendet, die Daten zurückgeben. In Bezug auf Syntax und Parameter sind die Builder identisch.
Wichtige Regel: Verwenden Sie async niemals für Fire-and-Forget. Wenn das Ergebnis der Coroutine nicht benötigt wird, verwenden Sie launch. async erzeugt Overhead durch die Erstellung von Deferred, der in solchen Szenarien nicht gerechtfertigt ist.
In Android wird async für parallele Operationen innerhalb von ViewModel und UseCases verwendet. Typische Szenarien: gleichzeitiges Laden von Profil und Nachrichtenfeed, Abrufen von Wetterdaten von mehreren Wetterdiensten, Batch-Datensynchronisation mit dem 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 }
}
}
}
Es wird empfohlen, eine Gruppe von async-Aufrufen in coroutineScope oder supervisorScope zu kapseln, um Fehler zu isolieren. supervisorScope ermöglicht die Fortsetzung der übrigen async-Coroutinen, selbst wenn eine fehlschlägt — nützlich für nicht kritische Operationen.
Häufig gestellte Fragen
Ja, aber es ist sinnlos. async ohne await() startet eine Coroutine, deren Ergebnis jedoch verloren geht. Wenn das Ergebnis nicht benötigt wird, verwenden Sie launch — es erstellt kein Deferred und ist speicher- und leistungseffizienter.
Eine Ausnahme innerhalb von async wird in Deferred gespeichert. Der Aufruf von await() löst diese Ausnahme aus. Zur Überprüfung ohne Auslösen verwenden Sie getCompletionExceptionOrNull(). Bei Verwendung von coroutineScope bricht ein Fehler alle untergeordneten Coroutinen ab.
awaitAll() — eine Erweiterungsfunktion für Iterable<Deferred<T>>, die die Coroutine anhält, bis alle Deferreds abgeschlossen sind. Gibt eine Ergebnisliste zurück. Wenn mindestens ein Deferred fehlschlägt, wird die Ausnahme ausgelöst und die restlichen werden abgebrochen.
Ja. async ist eine CoroutineScope-Erweiterungsfunktion, die an den Gültigkeitsbereich gebunden ist. GlobalScope.async erstellt eine Coroutine ohne Bindung an den Lebenszyklus — sie kann unbegrenzt ausgeführt werden. GlobalScope wird in Android aufgrund des Leckrisikos nicht empfohlen.
Die Anzahl wird vom Dispatcher bestimmt: Dispatchers.IO unterstützt bis zu 64 gleichzeitige Threads, Dispatchers.Default — bis zur Anzahl der CPU-Kerne. Verwenden Sie für Massenvorgänge begrenzte Parallelität über Semaphore oder mapNotNull mit awaitAll.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch