Sobes.tech
Middle

Τι συμβαίνει με μια coroutine όταν προκύψει μια εξαίρεση;

sobes.tech AI

Απάντηση από AI

Όταν προκύψει μια εξαίρεση μέσα σε μια coroutine, η εξάρτηση από τον τύπο της εξαίρεσης (και τις ρυθμίσεις dispatcher/περιβάλλοντος) καθορίζει πώς θα αντιμετωπιστεί.

  • Μη ελεγχόμενη εξαίρεση: Αν η εξαίρεση δεν πιαστεί μέσα στη coroutine, διαχέεται προς τα πάνω στην ιεραρχία των coroutines μέχρι το πλησιέστερο Job ή CoroutineScope. Η συμπεριφορά εξαρτάται από τον τύπο του Job:

    • Job: Από προεπιλογή, όταν προκύψει μια εξαίρεση σε μια παιδική coroutine, το γονικό Job ακυρώνεται, οδηγώντας στην ακύρωση όλων των άλλων παιδικών coroutines του. Η εξαίρεση διαχέεται προς τα πάνω.
    • SupervisorJob: Το γονικό SupervisorJob δεν ακυρώνεται όταν προκύψει εξαίρεση σε μια παιδική coroutine. Μόνο η coroutine στην οποία συνέβη η εξαίρεση ακυρώνεται. Αυτό είναι χρήσιμο όταν θέλεις η ακύρωση μιας παιδικής εργασίας να μην επηρεάζει τις άλλες.
    • CoroutineScope ριζικό: Αν η εξαίρεση φτάσει στο ριζικό CoroutineScope (π.χ. GlobalScope ή ένα scope που δημιουργήθηκε με CoroutineScope χωρίς Job), μεταβιβάζεται στον παγκόσμιο χειριστή εξαιρέσεων CoroutineExceptionHandler. Από προεπιλογή, αν δεν υπάρχει καταχωρημένος χειριστής, η εξαίρεση καταγράφεται στο System.err.
  • Πιασμένη εξαίρεση: Αν η εξαίρεση πιαστεί με ένα μπλοκ try-catch μέσα στη coroutine, αντιμετωπίζεται τοπικά και η coroutine συνεχίζει την εκτέλεση μετά το μπλοκ catch, αν αυτό είναι δυνατό. Η εξαίρεση δεν διαχέεται προς τα πάνω.

  • Εξαιρέσεις που σχετίζονται με την ακύρωση (π.χ. CancellationException): Αυτές οι εξαιρέσεις είναι ειδικές. Χρησιμοποιούνται για τον μηχανισμό ακύρωσης των coroutines και γενικά αγνοούνται από τους περισσότερους χειριστές εξαιρέσεων από προεπιλογή. Δεν προκαλούν καταστροφική αποτυχία του γονικού Job από προεπιλογή.

  • Εξαιρέσεις στο async: Η coroutine που ξεκινά με async τοποθετεί την εξαίρεση στο δικό της Deferred. Η εξαίρεση διαχέεται μόνο όταν καλέσεις .await() σε αυτό το Deferred. Μέχρι τότε, η εξαίρεση είναι "κρυμμένη".

import kotlinx.coroutines.*

fun main() = runBlocking {
    val scope = CoroutineScope(SupervisorJob())

    scope.launch {
        try {
            println("Η coroutine 1 ξεκίνησε")
            delay(100)
            throw RuntimeException("Κάτι πήγε στραβά στην coroutine 1")
        } catch (e: Exception) {
            println("Πιασμένη εξαίρεση στην coroutine 1: ${e.message}")
        }
    }

    scope.launch {
        println("Η coroutine 2 ξεκίνησε")
        delay(500)
        println("Η coroutine 2 τελείωσε")
    }

    delay(1000) // Περιμένουμε να δούμε την έξοδο
    scope.cancel() // Ακυρώνουμε το scope
}

Σε αυτό το παράδειγμα, η εξαίρεση στην Coroutine 1 πιάστηκε και η Coroutine 2 συνεχίζει την εκτέλεση επειδή χρησιμοποιείται SupervisorJob. Αν χρησιμοποιούνταν ένα κανονικό Job, η εξαίρεση στην Coroutine 1 θα οδηγούσε στην ακύρωση της Coroutine 2.