超时取消
大约 2 分钟
长时间运行的协程有时需要在经过一段时间后终止。虽然你可以手动持有对相应 Job 的引用,并在延迟后启动单独的协程来取消持有的协程,但是协程库提供了一个便利的函数 —— withTimeout。在 starter 项目中打开 WithTimeoutExample.kt 文件并编写以下代码:
fun main() = runBlocking {
withTimeout(1500L) {
repeat(1000) { i ->
println("$i. Crunching numbers [Beep.Boop.Beep]...")
delay(500L)
}
}
}
输出如下:
0. Crunching numbers [Beep.Boop.Beep]...
1. Crunching numbers [Beep.Boop.Beep]...
2. Crunching numbers [Beep.Boop.Beep]...
Exception in thread "main" kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1500 ms
...
withTimeout
抛出的 TimeoutCancellationException
是 CancellationException
的子类。你以前从未在控制台上看到过它的堆栈跟踪。那是因为,在取消的协程中,CancellationException
是被认为协程完成的正常原因。然而,在这个示例中你在 main 函数中使用了 withTimeout 函数。
因为取消只是一个异常,所以你可以以通常的方式关闭所有资源。如果你需要一些额外的操作,你可以将超时的代码包装在标准的 try/catch
块中。打开 TimeoutCancellationExceptionHandling.kt 并基于上面示例的代码添加 try/catch 块,如下所示:
fun main() = runBlocking {
try {
withTimeout(1500L) {
repeat(1000) { i ->
println("$i. Crunching numbers [Beep.Boop.Beep]...")
delay(500L)
}
}
} catch (e: TimeoutCancellationException) {
println("Caught ${e.javaClass.simpleName}")
}
}
输出如下:
0. Crunching numbers [Beep.Boop.Beep]...
1. Crunching numbers [Beep.Boop.Beep]...
2. Crunching numbers [Beep.Boop.Beep]...
Caught TimeoutCancellationException
或者,协程库提供了一个方便的 withTimeoutOrNull 函数。你可以使用它来存储函数计算的结果,如果超时则为 null。为了测试它的行为,打开 WithTimeoutOrNullExample.kt 文件并编写以下代码片段:
fun main() = runBlocking {
val result = withTimeoutOrNull(1300L) {
repeat(1000) { i ->
println("$i. Crunching numbers [Beep.Boop.Beep]...")
delay(500L)
}
"Done" // will get canceled before it produces this result
}
// Result will be `null`
println("Result is $result")
}
输出如下:
0. Crunching numbers [Beep.Boop.Beep]...
1. Crunching numbers [Beep.Boop.Beep]...
2. Crunching numbers [Beep.Boop.Beep]...
Result is null