Skip to content

A Kotlin extension that brings the elegance of `try-catch` to functional error handling—while **safely avoiding accidental capture of `CancellationException` in coroutines**. Use typed, chainable handlers with `runCatching` and never break structured concurrency again.

License

Notifications You must be signed in to change notification settings

Scarlet-Pan/try-catch

Repository files navigation

try-catch

A Kotlin extension that brings the elegance of try-catch to functional error handling—while safely avoiding accidental capture of CancellationException in coroutines. Use typed, chainable handlers with runCatching and never break structured concurrency again.

Maven Central Kotlin 1.6+ Test Status License


🤔 Why Use It?

In coroutine-based code, handling exceptions without breaking structured concurrency is essential. Consider these three approaches:

❌ Traditional try-catch — Swallows cancellation!

suspend fun fetchConfig(): Config = try {
    remoteConfigService.getConfig() // Suspending call that may throw IllegalStateException
} catch (e: Exception) { // ⚠️ Catches CancellationException — breaks structured concurrency!
    Log.w(TAG, "Failed to fetch config.", e)
    defaultConfig
}

⚠️ Using catch — Delicate API in coroutines

suspend fun fetchConfig(): Config = runCatching {
    remoteConfigService.getConfig() // Suspending call that may throw IllegalStateException
} catch { e -> // ❌ Avoid in coroutines: handles CancellationException (delicate API)
    Log.w(TAG, "Failed to fetch config.", e)
    defaultConfig
}

✅ Using catchNonCancel — Coroutine-safe recovery (recommended)

suspend fun fetchConfig(): Config = runCatching {
    remoteConfigService.getConfig() // Suspending call that may throw IllegalStateException
} catchNonCancel { e: IllegalStateException ->
    Log.w(TAG, "Invalid remote config state.", e)
    defaultConfig
} catchNonCancel { e ->
    Log.w(TAG, "Failed to fetch config.", e)
    defaultConfig
}

💡 Best Practice: Always prefer catchNonCancel over catch in suspend functions or any coroutine context.


📦 Install

Add the dependency to your build.gradle.kts:

dependencies {
    implementation("io.github.scarlet-pan:try-catch:1.0.0")
}

✅ Compatible with Kotlin 1.6+ and JVM 8+.
✅ Requires org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4 or higher.


🚀 Usage

Basic Usage

runCatching {
    // do something
} catch { e: IOException ->
    handleIOException(e)
} catch { e ->
    handle(e)
}

Supports chaining multiple typed exception handlers. Crucially, it allows you to distinguish whether kotlinx.coroutines.CancellationException should be handled or left to propagate.

In coroutine contexts, it is strongly recommended to use catchNonCancel instead of catch, to avoid accidentally swallowing cancellation signals and breaking structured concurrency.

Coroutine-Safe Handling with catchNonCancel

suspend fun fetchConfig(): Config = runCatching {
    remoteConfigService.getConfig() // Suspending call that may throw IllegalStateException
} catchNonCancel { e: IllegalStateException ->
    Log.w(TAG, "Invalid remote config state.", e)
    defaultConfig
} catchNonCancel { e ->
    Log.w(TAG, "Failed to fetch config.", e)
    defaultConfig
}
  • If the exception is a CancellationException, it is rethrown immediately.
  • Otherwise, the handler recovers with a fallback value.

📄 License

This project is licensed under the MIT License – see the LICENSE file for details.

Copyright © 2025 Scarlet Pan




—— 中文文档 Chinese Documentation ——




try-catch(中文)

一种能像传统 try-catch 一样优雅处理异常的 Kotlin 扩展,在协程中自动规避误捕获 CancellationException 的风险,让你在使用 runCatching 时既能享受类型安全的链式异常处理,又不会意外中断协程取消流程。

Maven Central Kotlin 1.6+ 测试状态 许可证


🤔 为什么使用它?

在协程代码中,处理异常的同时不破坏结构化并发至关重要。请看以下三种方式:

❌ 传统 try-catch — 会吞掉取消信号!

suspend fun fetchConfig(): Config = try {
    remoteConfigService.getConfig() // 可能抛出 IllegalStateException 的挂起调用
} catch (e: Exception) { // ⚠️ 会捕获 CancellationException — 破坏结构化并发!
    Log.w(TAG, "Failed to fetch config.", e)
    defaultConfig
}

⚠️ 使用 catch — 在协程中属于 delicate API

suspend fun fetchConfig(): Config = runCatching {
    remoteConfigService.getConfig() // 可能抛出 IllegalStateException 的挂起调用
} catch { e -> // ❌ 协程中应避免:会处理 CancellationException(delicate API)
    Log.w(TAG, "Failed to fetch config.", e)
    defaultConfig
}

✅ 使用 catchNonCancel — 协程安全的恢复方式(推荐)

suspend fun fetchConfig(): Config = runCatching {
    remoteConfigService.getConfig() // 可能抛出 IllegalStateException 的挂起调用
} catchNonCancel { e: IllegalStateException ->
    Log.w(TAG, "Invalid remote config state.", e)
    defaultConfig
} catchNonCancel { e ->
    Log.w(TAG, "Failed to fetch config.", e)
    defaultConfig
}

💡 最佳实践:在 suspend 函数或任何协程上下文中,始终优先使用 catchNonCancel 而非 catch


📦 安装

build.gradle.kts 中添加依赖:

dependencies {
    implementation("io.github.scarlet-pan:try-catch:1.0.0")
}

✅ 兼容 Kotlin 1.6+ 和 JVM 8+。
✅ 需要 org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4 或更高版本。


🚀 用法

基础用法

runCatching {
    // 执行某些操作
} catch { e: IOException ->
    handleIOException(e)
} catch { e ->
    handle(e)
}

支持链式调用多个类型化的异常处理器。关键在于:你可以明确区分是否应处理 kotlinx.coroutines.CancellationException

在协程上下文中,强烈建议使用 catchNonCancel 而非 catch,以避免意外吞掉取消信号,破坏结构化并发。

使用 catchNonCancel 实现协程安全的异常处理

suspend fun fetchConfig(): Config = runCatching {
    remoteConfigService.getConfig() // 可能抛出 IllegalStateException 的挂起调用
} catchNonCancel { e: IllegalStateException ->
    Log.w(TAG, "Invalid remote config state.", e)
    defaultConfig
} catchNonCancel { e ->
    Log.w(TAG, "Failed to fetch config.", e)
    defaultConfig
}
  • 若异常为 CancellationException,会立即重新抛出;
  • 否则,通过处理器返回兜底值进行恢复。

📄 许可证

本项目采用 MIT 许可证,详情请参见 LICENSE 文件。

版权所有 © 2025 Scarlet Pan

  • 贡献指南

也欢迎随时告诉我!

About

A Kotlin extension that brings the elegance of `try-catch` to functional error handling—while **safely avoiding accidental capture of `CancellationException` in coroutines**. Use typed, chainable handlers with `runCatching` and never break structured concurrency again.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages