0Pricing
Kotlin Academy · บทเรียน

catch และ onCompletion: การจัดการข้อผิดพลาดใน Flow

ดักจับ exception จากต้นทางและตอบสนองต่อเหตุการณ์ที่ Flow ทำงานเสร็จ

catch และ onCompletion: การจัดการข้อผิดพลาดใน Flow เป็นบทเรียน Kotlin Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Kotlin Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน

พื้นฐานข้อยกเว้นใน Flow

ข้อยกเว้นในโฟลว์จะทำให้โฟลว์สิ้นสุด หากไม่จัดการ ข้อยกเว้นจะแพร่ไปยังผู้รวบรวมข้อมูลและโฟลว์จะจบลง

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    try {
        flow {
            emit(1)
            throw RuntimeException("stream error")
            emit(2)
        }.collect { println(it) }
    } catch (e: Exception) {
        println("Caught: ${e.message}")
    }
}

ตัวดำเนินการ catch

catch จัดการข้อยกเว้นจากต้นทาง และสามารถส่งค่าทางเลือกออกมาหรือโยนข้อยกเว้นซ้ำได้ แต่จะไม่จับข้อยกเว้นที่ผู้รวบรวมข้อมูลเป็นผู้โยน

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flow {
        emit(1)
        throw RuntimeException("oops")
    }.catch { e ->
        println("Caught: ${e.message}")
        emit(-1)  // fallback value
    }.collect { println(it) } // 1, then -1
}

catch จัดการเฉพาะข้อยกเว้นจากต้นทาง

catch จะดักจับเฉพาะข้อยกเว้นจากตัวสร้างโฟลว์และตัวดำเนินการต้นทางเท่านั้น ไม่รวมข้อยกเว้นจากแลมบ์ดาของ collect

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    try {
        flowOf(1, 2, 3)
            .catch { e -> println("upstream: ${e.message}") }
            .collect {
                if (it == 2) throw RuntimeException("collector error") // not caught by catch
                println(it)
            }
    } catch (e: Exception) {
        println("Collector exception: ${e.message}")
    }
}

การโยนข้อยกเว้นซ้ำใน catch

คุณสามารถตรวจสอบข้อยกเว้น แล้วเลือกโยนซ้ำเฉพาะข้อยกเว้นที่ไม่สามารถจัดการได้

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    try {
        flow {
            emit(1)
            throw IllegalStateException("state error")
        }.catch { e ->
            if (e is RuntimeException) { emit(-1); return@catch }
            throw e  // rethrow unknown exceptions
        }.collect { println(it) }
    } catch (e: Exception) {
        println("Rethrown: ${e.message}")
    }
}

ตัวดำเนินการ onCompletion

onCompletion จะทำงานเมื่อโฟลว์เสร็จสิ้น ไม่ว่าจะเสร็จตามปกติหรือพร้อมข้อยกเว้น พารามิเตอร์ cause จะไม่เป็นค่าว่างเมื่อเกิดความล้มเหลว

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flowOf(1, 2, 3)
        .onCompletion { cause ->
            if (cause == null) println("Completed normally")
            else println("Failed: ${cause.message}")
        }
        .collect { println(it) }
}

onCompletion เมื่อเกิดข้อผิดพลาด

onCompletion จะทำงานแม้เกิดข้อยกเว้นจากต้นทาง โดยจะได้รับข้อยกเว้นใน cause แต่จะไม่ระงับข้อยกเว้นนั้น ข้อยกเว้นยังคงแพร่ต่อไป

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    try {
        flow {
            emit(1)
            throw RuntimeException("error")
        }.onCompletion { cause ->
            println("onCompletion cause=${cause?.message}")
        }.collect { println(it) }
    } catch (e: Exception) {
        println("outer catch: ${e.message}")
    }
}

การใช้ catch และ onCompletion ร่วมกัน

ใช้ catch เพื่อกู้คืนการทำงานและส่งค่าทางเลือกออกมา จากนั้นใช้ onCompletion เพื่อบันทึกข้อมูลหรือล้างทรัพยากรไม่ว่าผลลัพธ์จะเป็นอย่างไร

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flow {
        emit("ok")
        throw RuntimeException("network error")
    }.catch { e ->
        emit("fallback") // recover
    }.onCompletion {
        println("Stream done — release resources")
    }.collect { println(it) }
}

ตัวดำเนินการ retry

retry(n) { ... } จะสมัครรับข้อมูลจากโฟลว์ต้นทางใหม่เมื่อเกิดข้อยกเว้น โดยทำซ้ำได้ไม่เกิน n ครั้ง

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
var attempt = 0
fun unstableFlow() = flow {
    attempt++
    if (attempt < 3) throw RuntimeException("attempt $attempt failed")
    emit("success on attempt $attempt")
}
fun main() = runBlocking {
    unstableFlow()
        .retry(3) { e -> println("retrying: ${e.message}"); true }
        .collect { println(it) }
}

retryWhen สำหรับตรรกะแบบกำหนดเอง

retryWhen ให้ทั้งข้อยกเว้นและจำนวนครั้งที่ลอง เพื่อให้คุณเพิ่มการหน่วงเวลาแบบทวีคูณหรือกรองว่าข้อยกเว้นใดจะทำให้ลองใหม่

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    var n = 0
    flow {
        if (n++ < 2) throw IOException("IO error")
        emit("done")
    }.retryWhen { cause, attempt ->
        cause is IOException && attempt < 3
    }.collect { println(it) }
}

ตัวดำเนินการ onStart

onStart จะทำงานก่อนมีการส่งค่าครั้งแรก เหมาะสำหรับแสดงสถานะกำลังโหลดหรือเริ่มต้นทรัพยากรก่อนข้อมูลมาถึง

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flowOf("data")
        .onStart { emit("Loading...") }
        .onCompletion { emit("Done") }
        .collect { println(it) }
}

การจัดการข้อผิดพลาดในการเรียก API จริง

ครอบโฟลว์ของที่เก็บข้อมูลด้วย catch เพื่อแปลงข้อยกเว้นเป็นข้อผิดพลาดของโดเมน และใช้ onCompletion เพื่อซ่อนตัวบ่งชี้การโหลด

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
sealed class State { object Loading : State(); data class Data(val v: String) : State(); data class Error(val msg: String) : State() }
fun apiFlow(): Flow<String> = flow { delay(50); emit("response") }
fun uiFlow(): Flow<State> = apiFlow()
    .map { State.Data(it) as State }
    .onStart { emit(State.Loading) }
    .catch { e -> emit(State.Error(e.message ?: "unknown")) }
fun main() = runBlocking { uiFlow().collect { println(it) } }

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ข้อจำกัดสำคัญของโอเปอเรเตอร์ catch คืออะไร

สรุปทบทวน

การดักจับข้อยกเว้นจัดการข้อยกเว้นจากต้นทางและสามารถส่งค่าทางเลือกได้ ส่วน onCompletion จะทำงานเสมอ ทั้งเมื่อทำงานตามปกติหรือเกิดข้อผิดพลาด เพื่อดำเนินการล้างข้อมูลหรือบันทึกการทำงาน ใช้ร่วมกับ การลองใหม่/retryWhen เพื่อสร้างไปป์ไลน์โฟลว์ที่ทนทาน

คำถามที่พบบ่อย

บทเรียน “catch และ onCompletion: การจัดการข้อผิดพลาดใน Flow” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “catch และ onCompletion: การจัดการข้อผิดพลาดใน Flow” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Kotlin Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “catch และ onCompletion: การจัดการข้อผิดพลาดใน Flow”

ดักจับ exception จากต้นทางและตอบสนองต่อเหตุการณ์ที่ Flow ทำงานเสร็จ คุณปฏิบัติ Kotlin Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Kotlin Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Kotlin Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “catch และ onCompletion: การจัดการข้อผิดพลาดใน Flow” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Kotlin Academy นี้ได้ไหม

ได้ บทเรียน Kotlin Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ตัวดำเนินการ Flow: map, filter, transform และ take
  2. catch และ onCompletion: การจัดการข้อผิดพลาดใน Flow
  3. combine และ zip: รวม Flow หลายสาย
  4. flowOn และ buffer: บริบทและแรงดันย้อนกลับ
← กลับไปที่ Kotlin Academy