Zum Hauptinhalt springen

🎯 Coroutinen Grundlagen

📖 Erste Schritte mit Coroutinen

Um Coroutinen zu verwenden, müssen Sie zunächst die Coroutinen-Builder kennen. Lernen wir die drei grundlegendsten Builder kennen!

💡 runBlocking

Grundlegende Verwendung

import kotlinx.coroutines.*

fun main() = runBlocking { // Coroutine starten!
println("Hello")
delay(1000) // 1 Sekunde warten
println("World!")
}

Eigenschaften

fun main() {
println("Before")

runBlocking {
delay(1000)
println("Inside coroutine")
}

println("After") // Wartet, bis die Coroutine beendet ist
}
// Before
// (1 Sekunde warten)
// Inside coroutine
// After

Achtung: runBlocking blockiert den aktuellen Thread! Verwenden Sie es hauptsächlich nur in Tests oder in der main-Funktion.

🚀 launch

Asynchrone Aufgaben starten

fun main() = runBlocking {
launch {
delay(1000)
println("Task 1")
}

launch {
delay(500)
println("Task 2")
}

println("Main")
}
// Main
// (nach 500ms) Task 2
// (nach 1000ms) Task 1

Job-Rückgabe

fun main() = runBlocking {
val job = launch {
repeat(5) { i ->
println("Working... $i")
delay(500)
}
}

delay(1300)
println("Aufgabe abbrechen!")
job.cancel() // Aufgabe abbrechen
job.join() // Auf Abbruch warten
}

Verwaltung mehrerer Aufgaben

fun main() = runBlocking {
val jobs = List(5) { i ->
launch {
delay(1000L * i)
println("Aufgabe $i abgeschlossen")
}
}

jobs.forEach { it.join() } // Auf alle Aufgaben warten
println("Alle Aufgaben beendet!")
}

⚡ async

Ergebnisse empfangen

fun main() = runBlocking {
val deferred = async {
delay(1000)
return@async "Ergebniswert"
}

println("In Arbeit...")
val result = deferred.await() // Auf Ergebnis warten
println("Erhaltener Wert: $result")
}

launch vs async

fun main() = runBlocking {
// launch - kein Ergebnis
launch {
delay(1000)
println("launch abgeschlossen")
}

// async - Ergebnis zurückgeben
val result = async {
delay(1000)
"async abgeschlossen"
}

println(result.await())
}

🎯 Praktische Beispiele

Parallele Verarbeitung

import kotlin.system.measureTimeMillis

fun main() = runBlocking {
val time = measureTimeMillis {
val one = async { fetchData1() }
val two = async { fetchData2() }

println("Ergebnis: ${one.await()}, ${two.await()}")
}

println("Benötigte Zeit: ${time}ms") // ~1000ms (parallele Ausführung)
}

suspend fun fetchData1(): String {
delay(1000)
return "Daten1"
}

suspend fun fetchData2(): String {
delay(1000)
return "Daten2"
}

Sequenziell vs Parallel

fun main() = runBlocking {
// ❌ Sequenzielle Ausführung (langsam)
val time1 = measureTimeMillis {
val one = fetchData1()
val two = fetchData2()
println("$one, $two")
}
println("Sequenziell: ${time1}ms") // ~2000ms

// ✅ Parallele Ausführung (schnell)
val time2 = measureTimeMillis {
val one = async { fetchData1() }
val two = async { fetchData2() }
println("${one.await()}, ${two.await()}")
}
println("Parallel: ${time2}ms") // ~1000ms
}

Fehlerbehandlung

fun main() = runBlocking {
val deferred = async {
delay(1000)
throw Exception("Fehler aufgetreten!")
}

try {
deferred.await()
} catch (e: Exception) {
println("Fehler abgefangen: ${e.message}")
}
}

🔥 Nützliche Muster

Timeout

fun main() = runBlocking {
try {
withTimeout(1300) {
repeat(3) { i ->
println("Aufgabe $i")
delay(500)
}
}
} catch (e: TimeoutCancellationException) {
println("Zeitüberschreitung!")
}
}

Mehrere API-Aufrufe

data class UserData(
val profile: String,
val posts: List<String>,
val friends: List<String>
)

suspend fun loadUserData(userId: String): UserData = coroutineScope {
val profile = async { fetchProfile(userId) }
val posts = async { fetchPosts(userId) }
val friends = async { fetchFriends(userId) }

UserData(
profile.await(),
posts.await(),
friends.await()
)
}

suspend fun fetchProfile(id: String): String {
delay(500)
return "Profile($id)"
}

suspend fun fetchPosts(id: String): List<String> {
delay(800)
return listOf("Post1", "Post2")
}

suspend fun fetchFriends(id: String): List<String> {
delay(600)
return listOf("Friend1", "Friend2")
}

Wiederholungslogik

suspend fun <T> retryIO(
times: Int = 3,
delay: Long = 1000,
block: suspend () -> T
): T {
repeat(times - 1) { attempt ->
try {
return block()
} catch (e: Exception) {
println("Versuch ${attempt + 1} fehlgeschlagen")
delay(delay)
}
}
return block() // Letzter Versuch
}

fun main() = runBlocking {
val result = retryIO(times = 3) {
fetchDataFromAPI()
}
println(result)
}

var attempt = 0
suspend fun fetchDataFromAPI(): String {
attempt++
if (attempt < 3) {
throw Exception("Netzwerkfehler")
}
return "Erfolgreich!"
}

🛠️ coroutineScope

Strukturierte Nebenläufigkeit

suspend fun doWork() = coroutineScope {
launch {
delay(1000)
println("Aufgabe 1")
}

launch {
delay(2000)
println("Aufgabe 2")
}

println("Alle Aufgaben gestartet")
// Wartet, bis alle Kinder beendet sind
}

fun main() = runBlocking {
doWork()
println("Abgeschlossen!")
}

Fehlerfortpflanzung

suspend fun riskyWork() = coroutineScope {
launch {
delay(500)
throw Exception("Fehler!")
}

launch {
delay(1000)
println("Dieser Code wird nicht ausgeführt")
}
}

fun main() = runBlocking {
try {
riskyWork()
} catch (e: Exception) {
println("Fehler abgefangen: ${e.message}")
}
}

🤔 Häufig gestellte Fragen

F1. Was ist der Unterschied zwischen launch und async?

A: Ob ein Ergebnis zurückgegeben wird!

fun main() = runBlocking {
// launch - kein Ergebnis erforderlich
launch {
println("Einfache Aufgabe")
}

// async - Ergebnis erforderlich
val result = async {
"Rückgabewert"
}
println(result.await())
}

F2. Was ist der Unterschied zwischen join() und await()?

A: join wartet auf Abschluss, await wartet auf Ergebnis!

fun main() = runBlocking {
val job = launch {
delay(1000)
}
job.join() // Nur auf Abschluss warten

val deferred = async {
delay(1000)
"Ergebnis"
}
val result = deferred.await() // Auf Ergebnis warten
}

F3. Werden Coroutinen automatisch abgebrochen?

A: Wenn die Eltern-Coroutine abgebrochen wird, werden auch die Kind-Coroutinen abgebrochen!

fun main() = runBlocking {
val parent = launch {
val child = launch {
repeat(10) {
println("Kind-Aufgabe $it")
delay(500)
}
}

delay(1300)
println("Eltern abgeschlossen")
}

delay(2000)
parent.cancel() // Kind wird ebenfalls abgebrochen
}

🎬 Zusammenfassung

Sie haben die grundlegenden Coroutinen-Builder gemeistert!

Kernpunkte:
✅ runBlocking - Für Tests/main-Funktion
✅ launch - Aufgaben ohne Ergebnis
✅ async - Ergebnis zurückgeben
✅ coroutineScope - Strukturierte Nebenläufigkeit
✅ Leistungsverbesserung durch parallele Verarbeitung

Nächster Schritt: Lernen Sie in suspend-Funktionen die Suspend-Funktionen im Detail kennen!