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

CoroutineExceptionHandler: ตัวจัดการข้อผิดพลาดที่ไม่ถูกดักจับระดับ Global

ติดตั้ง CoroutineExceptionHandler เพื่อบันทึกหรือกู้คืนจาก exception ที่ไม่ถูกจัดการ

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

CoroutineExceptionHandler คืออะไร

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

import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { context, exception ->
    println("Caught unhandled: ${exception.message}")
}
fun main() = runBlocking {
    val scope = CoroutineScope(Dispatchers.Default + handler)
    scope.launch { throw RuntimeException("oops") }
    delay(100)
}

ใช้ได้เฉพาะกับโครูทีนระดับราก

CoroutineExceptionHandler จะจับข้อยกเว้นจากโครูทีน ระดับราก เท่านั้น (โครูทีนที่เปิดใช้งานโดยตรงบนขอบเขต) ส่วนโครูทีนลูกจะส่งต่อข้อยกเว้นไปยังโครูทีนแม่ ไม่ใช่ไปยังตัวจัดการ

import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { _, e -> println("Handler: ${e.message}") }
fun main() = runBlocking {
    // Root coroutine — handler fires:
    CoroutineScope(handler).launch {
        throw RuntimeException("root error")
    }
    delay(100)
    // NOT handler (child of coroutineScope):
    // launch { launch { throw RuntimeException() } }
}

ไม่ได้ระงับข้อยกเว้น

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

import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { _, e ->
    println("[CrashReport] ${e::class.simpleName}: ${e.message}")
    // send to Crashlytics, Sentry, etc.
}
fun main() = runBlocking {
    CoroutineScope(SupervisorJob() + handler).apply {
        launch { throw IllegalStateException("state error") }
        launch { delay(100); println("still alive") }
        delay(200)
        cancel()
    }
}

ตัวจัดการกับ SupervisorJob

การใช้ SupervisorJob + CoroutineExceptionHandler ร่วมกันเป็นรูปแบบมาตรฐานสำหรับขอบเขตที่มีอายุการใช้งานยาวนาน โครูทีนลูกจะล้มเหลวแยกจากกัน และความล้มเหลวที่ไม่มีการจัดการจะถูกบันทึกไว้

import kotlinx.coroutines.*
class AppScope {
    private val handler = CoroutineExceptionHandler { _, e ->
        println("Uncaught: ${e.message}")
    }
    val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default + handler)
}
fun main() = runBlocking {
    val app = AppScope()
    app.scope.launch { throw RuntimeException("task failed") }
    app.scope.launch { delay(100); println("other task ok") }
    delay(200)
    app.scope.cancel()
}

ตัวจัดการกับ async

สำหรับ async ข้อยกเว้นจะถูกเก็บไว้ใน Deferred และถูกส่งออกที่ await() ตัวจัดการจะไม่ทำงานสำหรับ async เว้นแต่จะไม่มีการรอ deferred นั้น

import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { _, e -> println("Handler: ${e.message}") }
fun main() = runBlocking {
    val scope = CoroutineScope(SupervisorJob() + handler)
    val deferred = scope.async { throw RuntimeException("async error") }
    try {
        deferred.await()  // exception thrown here
    } catch (e: RuntimeException) {
        println("Caught from await: ${e.message}")
    }
    delay(50)
    scope.cancel()
}

การเปรียบเทียบกับ Thread.UncaughtExceptionHandler

ต่างจาก UncaughtExceptionHandler ของ Java ตัวจัดการของ Kotlin เป็นส่วนหนึ่งของบริบทโครูทีน และมีผลเฉพาะกับโครูทีนภายในขอบเขตนั้นเท่านั้น

import kotlinx.coroutines.*
// Java style (applies to threads):
Thread.setDefaultUncaughtExceptionHandler { t, e ->
    println("Thread ${t.name} threw: ${e.message}")
}
// Kotlin coroutine style (applies to coroutines in scope):
val handler = CoroutineExceptionHandler { _, e ->
    println("Coroutine threw: ${e.message}")
}
fun main() = runBlocking { println("Handlers target different concurrency models") }

รูปแบบการบันทึกเหตุการณ์ด้วย MDC

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

import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { ctx, e ->
    val jobName = ctx[CoroutineName]?.name ?: "unknown"
    println("[${jobName}] ERROR: ${e.message}")
}
fun main() = runBlocking {
    CoroutineScope(SupervisorJob() + handler).launch(CoroutineName("DataLoader")) {
        throw RuntimeException("fetch failed")
    }
    delay(100)
}

การผสานรวมการรายงานการหยุดทำงาน

ใช้ตัวจัดการส่งต่อข้อยกเว้นที่ไม่มีการจับไปยังบริการรายงานการหยุดทำงาน เช่น Firebase Crashlytics หรือ Sentry

import kotlinx.coroutines.*
object CrashReporter {
    fun record(e: Throwable) = println("[Crashlytics] ${e.message}")
}
val handler = CoroutineExceptionHandler { _, e ->
    if (e !is CancellationException) CrashReporter.record(e)
}
val appScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + handler)
fun main() = runBlocking {
    appScope.launch { throw RuntimeException("unhandled in production") }
    delay(100)
    appScope.cancel()
}

การสืบทอดตัวจัดการ

ตัวจัดการจากบริบทแม่จะไม่ถูกสืบทอดโดยอัตโนมัติไปยังโครูทีนลูก ตัวจัดการต้องอยู่ในบริบทของโครูทีนระดับรากจึงจะทำงาน

import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { _, e -> println("Handler: ${e.message}") }
fun main() = runBlocking {
    // Handler only fires at root level:
    CoroutineScope(SupervisorJob() + handler).launch {
        // Child of root — exception propagates to root handler:
        launch { throw RuntimeException("nested") }
    }
    delay(100)
}

สรุปแนวทางปฏิบัติที่ดี

ติดตั้ง CoroutineExceptionHandler บนขอบเขตระดับแอปหรือระดับคุณลักษณะเสมอ บันทึกข้อยกเว้นที่ไม่ใช่การยกเลิกทุกกรณี อย่าพึ่งพาตัวจัดการนี้เพื่อควบคุมลำดับการทำงาน เพราะมีไว้สำหรับการสังเกตการณ์เท่านั้น

import kotlinx.coroutines.*
val globalHandler = CoroutineExceptionHandler { ctx, e ->
    if (e !is CancellationException) {
        println("[ERROR] ${ctx[CoroutineName]?.name}: ${e.message}")
    }
}
val appScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + globalHandler)
fun main() = runBlocking {
    appScope.launch(CoroutineName("Auth")) { throw RuntimeException("token expired") }
    delay(100); appScope.cancel()
}

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

CoroutineExceptionHandler ไม่ทำงานโดยอัตโนมัติกับโครูทีนประเภทใด

ทบทวน

CoroutineExceptionHandler เป็นตัวสังเกตการณ์ทางเลือกสุดท้ายสำหรับข้อยกเว้นที่ไม่มีการจับในโครูทีนระดับราก ใช้ร่วมกับ SupervisorJob สำหรับขอบเขตที่มีอายุการใช้งานยาวนาน ใช้สำหรับการบันทึกเหตุการณ์และการรายงานการหยุดทำงานเท่านั้น ไม่ใช่สำหรับควบคุมลำดับการทำงาน

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

บทเรียน “CoroutineExceptionHandler: ตัวจัดการข้อผิดพลาดที่ไม่ถูกดักจับระดับ Global” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “CoroutineExceptionHandler: ตัวจัดการข้อผิดพลาดที่ไม่ถูกดักจับระดับ Global”

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

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

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

บทเรียน “CoroutineExceptionHandler: ตัวจัดการข้อผิดพลาดที่ไม่ถูกดักจับระดับ Global” ใช้เวลานานแค่ไหน

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

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

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

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

  1. SupervisorJob กับ Job: การแยกความล้มเหลว
  2. CoroutineExceptionHandler: ตัวจัดการข้อผิดพลาดที่ไม่ถูกดักจับระดับ Global
  3. การส่งต่อ Exception ของ async/await
  4. การออกแบบสถาปัตยกรรม Coroutine ที่ทนทาน
← กลับไปที่ Kotlin Academy