Как обрабатываются исключение в Flow

Ответ

В Kotlin Flow исключения обрабатываются с помощью операторов catch и onCompletion.

Пример:

flow {
    emit(1)
    throw RuntimeException("Error!")
}
.catch { e -> 
    println("Caught exception: $e") 
    emit(-1) // можно эмитить fallback значение
}
.collect { value -> 
    println("Received: $value") 
}

Нюансы:

  • catch перехватывает исключения выше по цепочке, но не ниже.
  • onCompletion вызывается в любом случае (успех/ошибка), но не перехватывает исключения.
  • Для глобальной обработки можно использовать CoroutineExceptionHandler в корутине.

Если нужно перехватывать исключения в collect, оберни вызов в try-catch.

Ответ 18+ 🔞

Эй, смотри-ка, народ подтянулся! Давайте про Flows поговорим, а то у некоторых с исключениями там ёперный театр начинается, как в цирке с конями.

Вот представь: у тебя есть Flow, он эмитит данные, как миленький, а потом — бац! — RuntimeException("Error!"). И всё, пиздец, поток накрылся медным тазом, коллектор плачет, корутина дохнет. Не, ну так-то не пойдёт.

Спасение — операторы catch и onCompletion. Это как два охранника у двери в клуб: один ловит буянов (catch), а второй просто фиксирует, кто вошёл и кто вышел, даже если его побили (onCompletion).

Смотри, как catch работает, это просто песня:

flow {
    emit(1)
    throw RuntimeException("Error!")
}
.catch { e -> 
    println("Поймал исключение: $e") // Ёб твою мать, наконец-то!
    emit(-1) // Можно выдать запасное значение, типа "всё пропало, но держи -1"
}
.collect { value -> 
    println("Принял: $value") 
}

Запустишь — получишь "Принял: 1", а потом "Поймал исключение: java.lang.RuntimeException: Error!" и следом "Принял: -1". Поток не сдох, живой, коллектор доволен. Красота!

А теперь нюансы, блядь, без них никуда:

  • catch — он как хитрая жопа, ловит только то, что вылетело выше по цепочке, до него. Если ошибка случится в самом collect или в каком-нибудь map после catch — всё, приехали, не поймает. Доверия к нему, блять, ноль, если не знать этого правила.
  • onCompletion — это такой себе аккуратный бухгалтер. Его вызовут в любом случае: и если поток успешно завершился, и если его ёбнуло ошибкой. Он тебе скажет: "Всё, брат, поток закрывается. Вот причина, если была (cause)". Но! Он не перехватывает исключения. Он просто констатирует факт. Хочешь обработать — тебе в catch.
  • Если тебе надо глобально всё отлавливать, на уровне всей этой движухи, можно прикрутить CoroutineExceptionHandler к корутине, в которой всё это крутится. Это как поставить сетку над всем цирковым манежем.

И главный лайфхак, который многие забывают, а потом охуевают: если тебе нужно ловить косяки прямо в collect {...}, просто оберни этот блок в старый добрый try-catch. Да, просто и грубо, но работает.

try {
    myFlow.collect { value ->
        // Тут тоже может вылететь какая-нибудь залупа конская
        riskyOperation(value)
    }
} catch (e: Exception) {
    println("Ну всё, пизда рулю: $e")
}

Вот и вся магия. Главное — понять, кто (catch или onCompletion) за что отвечает, а не тыкать их наугад, как слепой котёнок. Тогда и волнение, блять, уйдёт, и потоки будут как шёлковые.