⏸️ suspend-Funktionen
📖 Was ist suspend?
suspend-Funktionen sind spezielle Funktionen, die Coroutines unterbrechen können. Sie sind effizient, weil sie warten können, ohne Threads zu blockieren!
💡 Grundkonzepte
Das suspend-Schlüsselwort
// suspend-Funktionen können andere suspend-Funktionen aufrufen
suspend fun doSomething() {
delay(1000) // suspend-Funktion
println("Fertig!")
}
fun main() = runBlocking {
doSomething() // Aufruf innerhalb einer Coroutine
}
Normale Funktion vs. suspend-Funktion
// ❌ Normale Funktionen können keine suspend-Funktionen aufrufen
fun normalFunction() {
// delay(1000) // Kompilierungsfehler!
}
// ✅ suspend-Funktionen können es
suspend fun suspendFunction() {
delay(1000) // OK!
}
🎯 Praxisbeispiele
Netzwerkanfragen
data class User(val id: String, val name: String)
suspend fun fetchUser(userId: String): User {
delay(1000) // Simulation von Netzwerkverzögerung
return User(userId, "Hong Gildong")
}
fun main() = runBlocking {
println("Anfrage gestartet")
val user = fetchUser("user123")
println("Empfangener Benutzer: ${user.name}")
}
Sequentielle Ausführung
suspend fun prepareFood(): String {
delay(1000)
return "Essen"
}
suspend fun setTable(): String {
delay(500)
return "Tisch"
}
suspend fun prepareMeal() {
println("Vorbereitung gestartet")
val food = prepareFood()
println("$food vorbereitet")
val table = setTable()
println("$table vorbereitet")
println("Alle Vorbereitungen abgeschlossen!")
}
fun main() = runBlocking {
val time = measureTimeMillis {
prepareMeal()
}
println("Benötigte Zeit: ${time}ms") // ~1500ms
}
Parallele Ausführung
suspend fun prepareMealParallel() = coroutineScope {
println("Vorbereitung gestartet")
val foodDeferred = async { prepareFood() }
val tableDeferred = async { setTable() }
val food = foodDeferred.await()
val table = tableDeferred.await()
println("$food, $table vorbereitet")
}
fun main() = runBlocking {
val time = measureTimeMillis {
prepareMealParallel()
}
println("Benötigte Zeit: ${time}ms") // ~1000ms (parallel!)
}
🔧 withContext
Dispatcher-Wechsel
suspend fun loadData(): String {
return withContext(Dispatchers.IO) {
// Ausführung im IO-Thread
delay(1000)
"Daten"
}
}
fun main() = runBlocking {
println("Haupt-Thread: ${Thread.currentThread().name}")
val data = loadData()
println("Zurück im Haupt-Thread: ${Thread.currentThread().name}")
println("Daten: $data")
}
Mehrere Kontexte
suspend fun processData() {
// IO-Operationen
val data = withContext(Dispatchers.IO) {
readFromFile()
}
// CPU-intensive Operationen
val processed = withContext(Dispatchers.Default) {
heavyComputation(data)
}
// Haupt-Thread (UI-Updates, etc.)
withContext(Dispatchers.Main) {
updateUI(processed)
}
}
suspend fun readFromFile(): String {
delay(500)
return "Dateiinhalt"
}
suspend fun heavyComputation(data: String): String {
delay(1000)
return data.uppercase()
}
suspend fun updateUI(data: String) {
println("UI-Update: $data")
}
🎨 Erweiterte Muster
Bedingte Wartezeit
suspend fun fetchDataIfNeeded(cache: String?): String {
return cache ?: fetchFromNetwork()
}
suspend fun fetchFromNetwork(): String {
delay(1000)
return "Neue Daten"
}
fun main() = runBlocking {
// Cache vorhanden - sofortige Rückgabe
val cached = fetchDataIfNeeded("Cache-Daten")
println(cached)
// Kein Cache - Netzwerkanfrage
val fresh = fetchDataIfNeeded(null)
println(fresh)
}
Verkettung
suspend fun step1(): Int {
delay(500)
return 10
}
suspend fun step2(input: Int): Int {
delay(500)
return input * 2
}
suspend fun step3(input: Int): Int {
delay(500)
return input + 5
}
suspend fun processChain(): Int {
val a = step1()
val b = step2(a)
val c = step3(b)
return c
}
fun main() = runBlocking {
val result = processChain()
println("Endergebnis: $result") // 25 (10 * 2 + 5)
}
Fehlerbehandlung
suspend fun riskyOperation(): String {
delay(1000)
throw Exception("Netzwerkfehler!")
}
suspend fun safeOperation(): String? {
return try {
riskyOperation()
} catch (e: Exception) {
println("Fehler aufgetreten: ${e.message}")
null
}
}
fun main() = runBlocking {
val result = safeOperation()
println("Ergebnis: ${result ?: "Fehlgeschlagen"}")
}
🔥 Praktische Funktionen
Timeout-Behandlung
suspend fun fetchWithTimeout(): String? {
return try {
withTimeout(2000) {
delay(3000) // Dauert zu lange
"Daten"
}
} catch (e: TimeoutCancellationException) {
println("Zeitüberschreitung!")
null
}
}
fun main() = runBlocking {
val result = fetchWithTimeout()
println("Ergebnis: ${result ?: "Timeout"}")
}
Wiederholungslogik
suspend fun <T> retry(
times: Int = 3,
initialDelay: Long = 100,
factor: Double = 2.0,
block: suspend () -> T
): T {
var currentDelay = initialDelay
repeat(times - 1) { attempt ->
try {
return block()
} catch (e: Exception) {
println("Versuch ${attempt + 1} fehlgeschlagen: ${e.message}")
}
delay(currentDelay)
currentDelay = (currentDelay * factor).toLong()
}
return block() // Letzter Versuch
}
var attemptCount = 0
suspend fun unstableAPI(): String {
attemptCount++
if (attemptCount < 3) {
throw Exception("Vorübergehender Fehler")
}
return "Erfolg!"
}
fun main() = runBlocking {
val result = retry(times = 5) {
unstableAPI()
}
println(result)
}
Cache-Muster
class DataRepository {
private var cache: String? = null
suspend fun getData(forceRefresh: Boolean = false): String {
if (!forceRefresh && cache != null) {
println("Rückgabe aus Cache")
return cache!!
}
println("Abrufen aus Netzwerk...")
delay(1000)
val data = "Neue Daten"
cache = data
return data
}
}
fun main() = runBlocking {
val repo = DataRepository()
// Erster Aufruf - Netzwerk
println(repo.getData())
// Zweiter Aufruf - Cache
println(repo.getData())
// Erzwungene Aktualisierung
println(repo.getData(forceRefresh = true))
}
🛡️ Abbrechbare Funktionen
Abbruchprüfung
suspend fun longRunningTask() {
repeat(10) { i ->
if (!isActive) {
println("Abgebrochen!")
return
}
println("Aufgabe $i")
delay(500)
}
}
fun main() = runBlocking {
val job = launch {
longRunningTask()
}
delay(2000)
println("Aufgabe abbrechen!")
job.cancelAndJoin()
}
ensureActive
suspend fun heavyWork() {
repeat(10) { i ->
ensureActive() // Wirft Exception bei Abbruch
println("Schwere Arbeit $i")
Thread.sleep(500) // Simulation von CPU-Arbeit
}
}
fun main() = runBlocking {
val job = launch {
try {
heavyWork()
} catch (e: CancellationException) {
println("Aufgabe wurde abgebrochen")
}
}
delay(2000)
job.cancel()
}
🤔 Häufig gestellte Fragen
F1. Erstellen suspend-Funktionen Threads?
A: Nein! suspend blockiert keine Threads, sondern unterbricht nur die Ausführung.
suspend fun example() {
println("Start: ${Thread.currentThread().name}")
delay(1000)
println("Ende: ${Thread.currentThread().name}")
}
// Kann im gleichen Thread ausgeführt werden!
F2. Was, wenn man alle Funktionen zu suspend macht?
A: Unnötig! Verwenden Sie es nur, wenn Sie asynchrone Operationen haben.
// ❌ Unnötig
suspend fun add(a: Int, b: Int) = a + b
// ✅ Notwendig
suspend fun fetchData() = withContext(Dispatchers.IO) {
// Netzwerkanfrage
}
F3. Können suspend-Funktionen normale Funktionen aufrufen?
A: Natürlich!
fun normalFunction() = println("Normale Funktion")
suspend fun suspendFunction() {
normalFunction() // OK!
delay(1000)
}
🎬 Zusammenfassung
Effizienter asynchroner Code mit suspend-Funktionen!
Wichtige Punkte:
✅ Unterbrechung mit suspend möglich
✅ Warten ohne Thread-Blockierung
✅ Thread-Wechsel mit withContext
✅ Steuerung von sequentieller/paralleler Ausführung
✅ Abbrechbare Aufgaben
Nächster Schritt: Erfahren Sie mehr über asynchrone Datenströme in Flow!