Async/Await ist ein Paar von Schlüsselwörtern zum Schreiben von asynchronem Code in synchronem Stil, verfügbar in Swift ab iOS 13 und in Kotlin mit Coroutinen. Laut Apple Swift Documentation, 2026 ersetzt Async/Await Callback-Ketten und GCD, wodurch asynchroner Code linear und lesbar wird. Das Schlüsselwort async markiert eine Funktion als asynchron, und await pausiert ihre Ausführung bis zum Erhalt des Ergebnisses.
Wichtige Punkte
Async/Await ist ein Sprachkonstrukt, das es ermöglicht, asynchronen Code genauso linear zu schreiben wie synchronen. In Swift erschien es mit iOS 13 / macOS 10.15 zusammen mit dem Swift Concurrency-Framework. In Kotlin ist async/await ab Version 1.3 über die Coroutinen-Bibliothek (kotlinx.coroutines) verfügbar.
Vor async/await wurde asynchroner Code auf Callbacks, GCD oder RxSwift aufgebaut. Ketten von verschachtelten Callbacks führten zur Callback-Hölle — einer tiefen Verschachtelung, die das Lesen und Debuggen erschwert. Async/Await löst dieses Problem, indem asynchrone Funktionen an await-Punkten “pausieren” und nach Erhalt des Ergebnisses wieder aufgenommen werden können.
Laut Google (2025) reduziert die Verwendung von async/await in Kotlin die Anzahl der Zeilen asynchronen Codes um 40–60% im Vergleich zum Callback-Ansatz. In Swift erreichte die Adoption von Swift Concurrency 2025 68% bei veröffentlichten Anwendungen. iOS 16 führte Swift Concurrency-Unterstützung in UIKit, SwiftUI und Foundation ein, was async/await zum Standardwerkzeug für alle neuen Apple-Projekte machte.
Der Hauptunterschied zwischen async/await und Threads ist die Pausierung ohne Blockierung. Wenn eine Funktion auf await trifft, pausiert sie und gibt den aktuellen Thread für andere Aufgaben frei. Nach Abschluss der asynchronen Operation wird die Funktion auf demselben oder einem anderen Thread fortgesetzt. Dies wird als kooperatives Multitasking bezeichnet.
Eine async-Funktion wird in eine Zustandsmaschine kompiliert — einen endlichen Automaten, der die Ausführungszustände verwaltet. Am await-Punkt speichert der Compiler den Kontext (lokale Variablen, Rücksprungadresse) und übergibt die Kontrolle an die Laufzeit. Wenn das Ergebnis bereit ist, stellt die Laufzeit den Kontext wieder her und setzt die Ausführung fort.
In Swift ist diese Zustandsmaschine auf Compiler-Ebene von SIL (Swift Intermediate Language) implementiert. In Kotlin wird der Continuation Passing Style (CPS)-Mechanismus verwendet: jede suspend-Funktion akzeptiert einen versteckten Continuation-Parameter, über den das Ergebnis zurückgegeben wird. Beide Ansätze garantieren eine sichere Wiederaufnahme ohne Speicherlecks.
Der Callback-Ansatz erfordert die Übergabe eines Closures, das nach Abschluss der Operation aufgerufen wird. Bei drei sequenziellen Anfragen erhält man drei Verschachtelungsebenen mit Fehlerbehandlung auf jeder Ebene. Async/Await verwandelt dieselben drei Anfragen in drei sequenzielle Zeilen mit einem einzigen catch-Block.
GCD (DispatchQueue) löst die Callback-Hölle durch serielle Warteschlangen und DispatchGroup, bleibt aber ausführlich. Für eine einfache Aufgabe — Benutzer laden, dann Freunde, dann UI — sind Warteschlangen, Gruppen und notify-Blöcke erforderlich. Async/Await erledigt dieselbe Aufgabe mit drei Zeilen in einer einzigen Funktion.
| Aspekt | Callback | GCD | Async/Await |
|---|---|---|---|
| Verschachtelung | Tief (Pyramide des Untergangs) | Mittel (Gruppen+Benachrichtigung) | Linear (flach) |
| Fehlerbehandlung | Pro Callback | Manuell | Einheitlich (try/catch) |
| Abbruch | Manuell | Eingeschränkt | Task.cancel() |
| Thread-Sicherheit | Manuell | Über serielle Warteschlange | Über MainActor |
Laut Apple WWDC 2024 ist Swift Concurrency async/await der empfohlene Ansatz für neuen Code. GCD bleibt für die Integration mit C-Bibliotheken und spezifische Low-Level-Thread-Verarbeitungsszenarien bestehen.
Task ist die grundlegende Einheit asynchroner Arbeit in Swift. Ein Task wird im Kontext eines vorhandenen Actors oder auf einem beliebigen Thread erstellt. Innerhalb eines Tasks können async-Funktionen über await aufgerufen werden. TaskGroup ermöglicht das parallele Starten mehrerer untergeordneter Aufgaben und das Sammeln ihrer Ergebnisse — eine verbesserte Version von DispatchGroup.
Actor ist ein threadsicherer Referenztyp, der seinen Zustand vor Datenrennen schützt. Der Compiler garantiert, dass der Zugriff auf Actor-Eigenschaften nur über async-Aufrufe oder innerhalb der Actor-Isolation möglich ist. Actor ersetzt die serielle DispatchQueue zum Schutz des gemeinsam genutzten veränderlichen Zustands ohne manuelle Sperren.
Das Prinzip der Structured Concurrency besagt: Jede asynchrone Aufgabe hat einen Parent, und der Parent wird erst abgeschlossen, wenn alle seine Children abgeschlossen sind. Wenn Aufgabe A Task { await B() } startet, wartet A auf B. Dies verhindert Task-Leaks und gewährleistet einen vorhersagbaren Lebenszyklus.
In Kotlin wird async/await durch Coroutinen implementiert — leichtgewichtige Threads der Sprache. Eine Funktion wird mit dem Schlüsselwort suspend markiert (analog zu Swifts async). Zum Starten wird launch (Feuer-und-Vergessen) oder async (mit Ergebnis) verwendet. Await wird aufgerufen, um das Ergebnis von async zu erhalten.
Dispatcher bestimmt, auf welchem Thread-Pool die Coroutine ausgeführt wird: Dispatchers.Main für UI, Dispatchers.IO für Netzwerk/Datenträger und Dispatchers.Default für CPU-intensive Aufgaben. Structured Concurrency wird durch CoroutineScope gewährleistet — bei Abbruch des Scopes werden alle untergeordneten Coroutinen abgebrochen.
Laut JetBrains (2025) verwenden 97% der Android-Apps im Google Play Store kotlinx.coroutines, und 82% von ihnen nutzen async/await für Netzwerkanfragen. Coroutinen sind zum De-facto-Standard für asynchrone Programmierung in Android geworden. Kotlin Multiplatform unterstützt ebenfalls Coroutinen und ermöglicht die Verwendung gemeinsam genutzten asynchronen Codes auf Android, iOS und der Serverseite.
Betrachten wir zwei Beispiele für jede Sprache: sequenzielles Laden von Daten und parallele Anfragen mit TaskGroup oder async/await.
await pausiert die Ausführung bis zum Empfang der Benutzerdaten, dann bis zum Empfang der Freunde. Keine verschachtelten Closures.
func loadProfile() async throws -> Profile {
let user = try await api.fetchUser()
let friends = try await api.fetchFriends(for: user.id)
return Profile(user: user, friends: friends)
}
TaskGroup startet Aufgaben parallel und sammelt die Ergebnisse in einem Array. Die Reihenfolge der Ergebnisse kann von der Startreihenfolge abweichen.
func loadParallel() async throws -> [String] {
await withThrowingTaskGroup(of: String.self) { group in
group.addTask { try await api.fetchName() }
group.addTask { try await api.fetchEmail() }
group.addTask { try await api.fetchAvatar() }
var results = [String]()
for try await value in group { results.append(value) }
return results
}
}
suspend-Funktionen können andere suspend-Funktionen aufrufen. Analog zu Swift: sequenzielle Aufrufe.
suspend fun loadProfile(): Profile {
val user = api.fetchUser()
val friends = api.fetchFriends(user.id)
return Profile(user, friends)
}
In Kotlin wird Parallelität durch coroutineScope mit async für jede Anfrage erreicht.
suspend fun loadParallel(): List<String> = coroutineScope {
val name = async { api.fetchName() }
val email = async { api.fetchEmail() }
val avatar = async { api.fetchAvatar() }
listOf(name.await(), email.await(), avatar.await())
}
Vergessener Task { } — eine async-Funktion kann nicht aus einem synchronen Kontext ohne Umhüllung in einem Task aufgerufen werden. In Swift wird dies mit einem Fehler kompiliert. In Kotlin ist ein Start über lifecycleScope oder viewModelScope erforderlich. Der Versuch, eine suspend-Funktion von einer normalen Funktion aus aufzurufen, wird nicht kompiliert.
Nicht jede Funktion muss asynchron sein. CPU-intensive Aufgaben ohne E/A profitieren nicht von async — sie sollten besser auf DispatchQueue mit .userInitiated QoS ausgeführt werden. Async/Await ist für I/O-gebundene Operationen optimiert: Netzwerk, Datenträger, Warten.
Die Verwendung von .result auf dem Hauptthread (synchrones Warten auf das Ergebnis einer async-Funktion) führt zu Blockierung. In Swift wird Task.synchronousWait von Apple nicht empfohlen. In Kotlin ist runBlocking auf dem Hauptthread ein Antipattern — verwenden Sie lifecycleScope.
Wenn ein Task nicht in einer Klassen-Eigenschaft gespeichert wird, wird er nach Verlassen des Kontexts abgebrochen. In Swift ist Task bereichsgebunden — sein Lebenszyklus ist an den erstellenden Kontext gebunden. Speichern Sie den Task in einer Eigenschaft, wenn Sie eine langlebige Operation benötigen.
Häufig gestellte Fragen
Async/Await ist ein Sprachkonstrukt, das keine Erstellung von Warteschlangen erfordert. DispatchQueue ist eine System-API zur Verwaltung von Threads. Async/await wird auf Sprachebene in eine Zustandsmaschine kompiliert, GCD — auf OS-Ebene in Syscalls.
Nein — await pausiert die Funktion, gibt aber den Thread für andere Aufgaben frei. Nach Abschluss der Operation wird die Funktion auf einem beliebigen verfügbaren Thread aus dem Pool fortgesetzt.
Ja — über Task.cancel() in Swift oder Job.cancel() in Kotlin. Der Abbruch propagiert gemäß Structured Concurrency auf alle untergeordneten Aufgaben. Der Code prüft das Abbruch-Flag über Task.isCancelled oder ensureActive().
MainActor ist ein Actor, dessen Eigenschaften und Methoden immer auf dem Hauptthread ausgeführt werden. Durch Markieren einer Funktion mit @MainActor garantieren Sie, dass UI-Updates auf dem richtigen Thread erfolgen, und ersetzen DispatchQueue.main.async.
Async/Await ist verfügbar in Swift (iOS 13+), Kotlin (über kotlinx.coroutines), Dart (Flutter), JavaScript/TypeScript, Python, C#, Rust und Go (über Goroutines mit syntaktischem Zucker).
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