Async/Await: Wesen, async-Funktionen und Arbeiten mit await

Autor: IT Sectr Veröffentlicht: 2026-03-16 Lesezeit: 8 Min.

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 syntaktischer Zucker für asynchronen Code ohne verschachtelte Callbacks
  • async markiert eine Funktion als asynchron und pausierbar
  • await pausiert die Ausführung der Funktion bis zum Abschluss der asynchronen Operation
  • Task in Swift erstellt eine neue asynchrone Arbeitseinheit mit Kontext
  • Structured Concurrency garantiert, dass alle untergeordneten Aufgaben vor der übergeordneten abgeschlossen werden

Was ist Async/Await und asynchrone Programmierung?

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.

Funktionsprinzip: pausierbare Funktionen

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.

Wie Async/Await funktioniert: Pausierung und Wiederaufnahme

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.

  • Swift — der Compiler übersetzt die async-Funktion in SIL mit einem runAsync-Coroutine-Frame
  • Kotlin — jede suspend-Funktion erhält einen Continuation und delegiert die Ausführung an den Dispatcher
  • Beide Sprachen — geben den Thread bei await frei, blockieren ihn nicht während des Wartens
  • Ausnahmen — werden über Standardmechanismen propagiert (throws / try-catch)

Async/Await vs Callbacks und GCD: Ansatzvergleich

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.

AspektCallbackGCDAsync/Await
VerschachtelungTief (Pyramide des Untergangs)Mittel (Gruppen+Benachrichtigung)Linear (flach)
FehlerbehandlungPro CallbackManuellEinheitlich (try/catch)
AbbruchManuellEingeschränktTask.cancel()
Thread-SicherheitManuellÜ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.

Swift Concurrency: Task, TaskGroup und Actor

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.

Structured Concurrency

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.

Async/Await in Kotlin mit Coroutinen

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.

Codebeispiele mit Async/Await in Swift und Kotlin

Betrachten wir zwei Beispiele für jede Sprache: sequenzielles Laden von Daten und parallele Anfragen mit TaskGroup oder async/await.

Swift: Sequenzielle async-Aufrufe

await pausiert die Ausführung bis zum Empfang der Benutzerdaten, dann bis zum Empfang der Freunde. Keine verschachtelten Closures.

swift
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)
}

Swift: Parallele Anfragen mit TaskGroup

TaskGroup startet Aufgaben parallel und sammelt die Ergebnisse in einem Array. Die Reihenfolge der Ergebnisse kann von der Startreihenfolge abweichen.

swift
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
    }
}

Kotlin: Sequenzieller Aufruf mit async/await

suspend-Funktionen können andere suspend-Funktionen aufrufen. Analog zu Swift: sequenzielle Aufrufe.

kotlin
suspend fun loadProfile(): Profile {
    val user = api.fetchUser()
    val friends = api.fetchFriends(user.id)
    return Profile(user, friends)
}

Kotlin: Parallele Anfragen mit async/await

In Kotlin wird Parallelität durch coroutineScope mit async für jede Anfrage erreicht.

kotlin
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())
}

Häufige Fehler und Einschränkungen von Async/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.

Übermäßige Verwendung von async ohne Notwendigkeit

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.

Blockieren des Hauptthreads durch .result

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.

Task-Leck bei Abbruch

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

Wie unterscheidet sich async/await von DispatchQueue?

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.

Blockiert await den Thread?

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.

Kann eine async-Aufgabe abgebrochen werden?

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().

Was ist MainActor in Swift?

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.

In welchen Sprachen gibt es async/await?

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

  • Async/Await ist ein Sprachkonstrukt für linearen asynchronen Code ohne Callback-Hölle
  • Swift Concurrency bietet Task, TaskGroup, Actor und Structured Concurrency
  • Kotlin-Coroutinen verwenden suspend-Funktionen, Dispatcher und Coroutine-Scopes
  • Pausierung ohne Blockierung ist der Hauptunterschied von async/await zu Threads und Warteschlangen
  • Structured Concurrency garantiert den Abschluss aller untergeordneten Aufgaben vor der übergeordneten
  • Async/Await vs GCD — ersteres für neuen Code bevorzugt, letzteres für Legacy und Low-Level
  • MainActor in Swift und Dispatchers.Main in Kotlin schützen den UI-Thread vor Hintergrundoperationen

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.

Projekt besprechen

Lesen Sie auch