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.
In coroutine-based code, handling exceptions without breaking structured concurrency is essential. Consider these three approaches:
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
}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
}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
catchNonCancelovercatchinsuspendfunctions or any coroutine context.
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+.
✅ Requiresorg.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4or higher.
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.
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.
This project is licensed under the MIT License – see the LICENSE file for details.
Copyright © 2025 Scarlet Pan
—— 中文文档 Chinese Documentation ——
一种能像传统 try-catch 一样优雅处理异常的 Kotlin 扩展,在协程中自动规避误捕获 CancellationException 的风险,让你在使用 runCatching 时既能享受类型安全的链式异常处理,又不会意外中断协程取消流程。
在协程代码中,处理异常的同时不破坏结构化并发至关重要。请看以下三种方式:
suspend fun fetchConfig(): Config = try {
remoteConfigService.getConfig() // 可能抛出 IllegalStateException 的挂起调用
} catch (e: Exception) { // ⚠️ 会捕获 CancellationException — 破坏结构化并发!
Log.w(TAG, "Failed to fetch config.", e)
defaultConfig
}suspend fun fetchConfig(): Config = runCatching {
remoteConfigService.getConfig() // 可能抛出 IllegalStateException 的挂起调用
} catch { e -> // ❌ 协程中应避免:会处理 CancellationException(delicate API)
Log.w(TAG, "Failed to fetch config.", e)
defaultConfig
}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,以避免意外吞掉取消信号,破坏结构化并发。
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
- 贡献指南
也欢迎随时告诉我!