Suspend function ist eine Funktion mit dem suspend-Modifikator, die ihre Ausführung anhalten kann, ohne einen Thread zu blockieren, und später in derselben Koroutine fortsetzen kann. Laut JetBrains Kotlin Docs, 2025 sind Suspend-Funktionen ein grundlegender Baustein von Koroutinen und bieten Asynchronität ohne Callbacks. Jede Suspend-Funktion wird in eine auf Continuation basierende Zustandsmaschine kompiliert, die eine effiziente Verwaltung von Aussetzungspunkten ermöglicht.
Wichtige Punkte
Suspend function ist eine mit dem suspend-Schlüsselwort deklarierte Funktion, die die Ausführung an einem oder mehreren Punkten anhalten kann, ohne den Thread zu blockieren. Jeder Aufruf einer Suspend-Funktion innerhalb einer anderen Suspend-Funktion ist ein potenzieller Aussetzungspunkt.
suspend fun fetchUserData(): User {
val response = httpClient.get("/user")
return parser.parse(response)
}
Der Kotlin-Compiler übersetzt eine solche Funktion in eine Zustandsmaschine. Jeder Aussetzungspunkt (ein Aufruf einer anderen Suspend-Funktion) wird zu einem Zustand (Label). Der aktuelle Thread wird zwischen den Zuständen freigegeben, und nach Abschluss der erwarteten Operation wird die Ausführung vom nächsten Zustand aus fortgesetzt.
Suspend-Funktionen erschienen in Kotlin 1.3 (2018) zusammen mit Koroutinen als experimentelles Feature und wurden in Kotlin 1.5 (2021) stabil. Davor wurde Asynchronität in Kotlin/Java durch Callbacks, RxJava und CompletableFuture erreicht. Suspend-Funktionen boten eine Alternative mit linearer Syntax und automatischer Thread-Verwaltung.
Das Verständnis der inneren Arbeitsweise von Suspend-Funktionen ist der Schlüssel zur korrekten Arbeit mit Koroutinen. Im Gegensatz zu regulären Funktionen wird jede Suspend-Funktion in eine Klasse mit dem Continuation-Interface kompiliert.
Der Kotlin-Compiler fügt am Ende jeder Parameterliste einer Suspend-Funktion einen Parameter vom Typ Continuation hinzu. Continuation enthält:
Angenommen, wir haben eine Suspend-Funktion mit zwei Aufrufen anderer Suspend-Funktionen:
suspend fun process() {
val a = stepOne()
val b = stepTwo(a)
println(b)
}
Der Compiler wandelt sie in eine Zustandsmaschine mit Labels um:
// Simplified generated code representation
fun process(cont: Continuation<Unit>): Any? {
val cont = cont as ProcessContinuation
when (cont.label) {
0 -> {
cont.label = 1
if (stepOne(cont) == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
}
1 -> {
cont.label = 2
val a = cont.result as TypeA
if (stepTwo(a, cont) == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
}
2 -> {
println(cont.result)
Unit
}
}
}
Wichtige Beobachtung: Wenn die Funktion COROUTINE_SUSPENDED zurückgibt, wird der aktuelle Thread freigegeben. Wenn die asynchrone Operation abgeschlossen ist, wird Continuation.resumeWith aufgerufen, und die Zustandsmaschine fährt mit dem nächsten Label fort.
Die Deklaration einer Suspend-Funktion unterscheidet sich nicht von einer regulären Funktion, außer durch das Schlüsselwort suspend vor fun. Es gibt nur eine Einschränkung: Eine Suspend-Funktion kann nur von einer Koroutine oder einer anderen Suspend-Funktion aufgerufen werden.
suspend fun delayAndReturn(ms: Long): String {
delay(ms)
return "Done after ${ms}ms"
}
In diesem Beispiel ist delay ebenfalls eine Suspend-Funktion, die die Koroutine für die angegebene Anzahl von Millisekunden anhält, ohne den Thread zu blockieren. Nach der Verzögerung wird die Ausführung fortgesetzt.
fun main() = runBlocking {
val result = delayAndReturn(1000)
println(result)
}
runBlocking erstellt eine Brücke zwischen der normalen Welt und Koroutinen. Innerhalb des Lambdas können beliebige Suspend-Funktionen aufgerufen werden.
Kotlin unterstützt Suspend-Versionen funktionaler Typen — suspend () -> T und suspend (A) -> B. Dies ermöglicht die Übergabe asynchroner Lambdas an Funktionen höherer Ordnung:
suspend fun withRetry (
retries: Int = 3,
block: suspend () -> T
): T {
repeat(retries - 1) {
try { return block() }
catch (_: Exception) { delay(100) }
}
return block()
}
Die Funktion withRetry nimmt ein Suspend-Lambda entgegen und wiederholt dessen Ausführung bei Fehlern. Dies ist ein typisches Muster für Netzwerkanfragen mit Wiederholungsversuchen.
Die Unterschiede zwischen Suspend- und regulären Funktionen gehen über das bloße Hinzufügen eines Modifikators hinaus. Betrachten wir die wesentlichen Unterschiede.
| Eigenschaft | Reguläre Funktion | Suspend-Funktion |
|---|---|---|
| Ausführungs-Thread | Blockiert den Thread bis zur Fertigstellung | Kann Thread freigeben und später fortsetzen |
| Compiler-Parameter | Nur angegebene Parameter | Implizites Continuation am Ende |
| Aufruf aus regulärer Funktion | Ja | Nein |
| Stack | Physischer Thread-Stack | Zustandsmaschine im Heap + physischer Stack zwischen Punkten |
| Rückgabewert | Direkter Wert | Wert oder COROUTINE_SUSPENDED |
| Leistung | Minimaler Overhead | ~einige Nanosekunden pro Zustandsmaschine (Kotlin 1.9+) |
Eine reguläre Funktion hat kein Continuation — sie hat keinen Ort, um den Zustand zu speichern, und nichts, um die Ausführung fortzusetzen. Wenn Sie eine Suspend-Funktion von einer regulären Funktion aus aufrufen müssen, verwenden Sie runBlocking (für Tests) oder CoroutineScope.launch (für die Produktion mit Lebenszyklus-Bewusstsein).
Betrachten wir drei reale Szenarien für die Verwendung von Suspend-Funktionen in Android-Apps mit Kotlin.
Room unterstützt Suspend-Funktionen direkt — die Abfrage wird automatisch im Hintergrundthread ausgeführt:
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getUser(id: Int): User?
@Insert
suspend fun insertUser(user: User)
}
Room verwendet intern Dispatchers.IO zur Ausführung der Abfrage, und das Ergebnis wird auf dem Dispatcher zurückgegeben, auf dem die Suspend-Funktion aufgerufen wurde.
class ProfileViewModel : ViewModel() {
private val repo = ProfileRepository()
fun loadProfile(id: String) {
viewModelScope.launch {
val profile = repo.getProfile(id)
_profile.value = profile
}
}
}
ViewModelScope.launch erstellt eine Koroutine, in der die Suspend-Funktion getProfile aufgerufen wird. Nach Erhalt des Ergebnisses wird die UI im Hauptthread aktualisiert.
suspend fun placeOrder(cart: Cart): OrderResult {
val validated = validateCart(cart)
val payment = processPayment(validated)
val receipt = sendReceipt(payment)
return receipt
}
Drei Suspend-Funktionen werden nacheinander ausgeführt. Bei jedem Schritt kann die Koroutine anhalten, ohne den Thread zu blockieren. Wenn ein Schritt eine Ausnahme auslöst, werden die übrigen Schritte nicht ausgeführt, was vor falschen Bestellungszuständen schützt.
Selbst erfahrene Kotlin-Entwickler machen Fehler beim Entwurf von Suspend-Funktionen. Betrachten wir die häufigsten.
Eine Suspend-Funktion macht Code nicht automatisch asynchron. Thread.sleep(), InputStream.read() und andere blockierende Aufrufe blockieren weiterhin den Thread. Verwenden Sie withContext(Dispatchers.IO), um blockierende Operationen zu kapseln.
Wenn eine Funktion keine anderen Suspend-Funktionen aufruft und keine asynchronen Operationen ausführt — ist der suspend-Modifikator überflüssig. Er fügt Overhead für die Zustandsmaschine hinzu und schränkt den Aufrufkontext ein. Machen Sie eine Funktion nur dann zu suspend, wenn sie tatsächlich anhält.
Wenn eine Koroutine abgebrochen wird, werfen Suspend-Funktionen CancellationException. Fangen Sie sie nicht gedankenlos ab — Sie berauben den aufrufenden Code der Fähigkeit, den Abbruch korrekt abzuschließen. Wenn Sie eine Abschlussoperation durchführen müssen, verwenden Sie einen finally-Block und NonCancellable.
suspend fun safeOperation() {
try {
doWork()
} finally {
withContext(NonCancellable) {
cleanup()
}
}
}
Der finally-Block wird immer ausgeführt, auch bei Abbruch, und NonCancellable stellt sicher, dass die Bereinigung nicht unterbrochen wird.
Sie können eine Suspend-Funktion nicht direkt aus einem Callback aufrufen, ohne eine Koroutine zu erstellen. Verwenden Sie suspendCoroutine oder suspendCancellableCoroutine, um den Callback-Stil an Koroutinen anzupassen.
Häufig gestellte Fragen
Ja, technisch gesehen kann eine Suspend-Funktion keine anderen Suspend-Funktionen aufrufen. Der Compiler erstellt eine Zustandsmaschine mit einem einzigen Zustand (Label 0). Allerdings hat eine solche Funktion keinen praktischen Nutzen — sie wird wie eine reguläre ausgeführt, jedoch mit Overhead. Verwenden Sie suspend nicht unnötig.
Kotlin bietet kotlinx-coroutines-debug — eine Bibliothek mit DebugProbes und Koroutinen-Tracing-Werkzeugen. In Android Studio ab Arctic Fox gibt es einen integrierten Coroutines-Tab im Debugger, der aktive Koroutinen, ihren Zustand und Aussetzungspunkte anzeigt.
Jeder Aussetzungspunkt erzeugt einen neuen Zustand in der Zustandsmaschine. Für die meisten Anwendungen beträgt der Overhead eines Punktes wenige Nanosekunden (Kotlin 1.9+). Nur bei Zehntausenden von Punkten in einer Schleife sollten Sie in Betracht ziehen, Operationen zu kombinieren oder sequence/flow zu verwenden.
In Kotlin ist suspend ein Funktionsmodifikator, kein Rückgabewert-Marker (wie async in C#). Jede Suspend-Funktion kann beliebige Parameter und Rückgabetypen haben, und ihr Aufruf unterscheidet sich syntaktisch nicht von einem regulären Funktionsaufruf — es gibt keinen await-Operator an der Aufrufstelle.
Verwenden Sie suspendCancellableCoroutine zur Anpassung. Darin registrieren Sie einen Callback, der continuation.resume() aufruft, und geben einen Abbruchtoken zurück, wenn der Callback das Abbestellen unterstützt. Dies ist das Standardmuster zum Umwickeln alter Android-APIs.
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