如何让withTimeoutOrNull返回null但在代码块中完成代码

时间:2018-07-17 12:25:12

标签: kotlin-coroutines

我需要我的代码来运行一个块,并在1秒后返回值以防超时,但是让它完成工作。 我设法实现了一些可行的方法,但IDE建议将async替换为withContext(DefaultDispatcher),但不能正常工作。

所以我的问题是如何使它在没有IDE警告的情况下工作。我是Kotlin Coroutines的新手,所以我可能在这里错过了一些东西。

@Test
fun how_timeout_with_null_works() = runBlocking<Unit> {
    val time = measureTimeMillis {
        println("Start test")
        val result = withTimeoutOrNull(1, TimeUnit.SECONDS) {
            async { doSomeHardWork() }.await()
        }
        println("End test $result")
    }
    println("Time $time")
    delay(3000)
}

private suspend fun doSomeHardWork(): String {
    println("start hard work")
    Thread.sleep(2000)
    print("end hard work")
    return "[hard work done]"
}

1 个答案:

答案 0 :(得分:4)

在这种情况下,IDE会发出警告,因为async(ctx) { ... }.await()通常是一个错误,而withContext(ctx) { ... }通常可以更好地反映代码作者的初衷。

对于您的代码,您的意图是不同的。您的意图是等待 1秒钟,而不限制您的doSomeHardWork代码。但是,代码的结构不能反映您的意图。您已将整个代码块包装到withTimeout中,并将doSomeHardWork放入其中,而您的意图只是对其进行时间限制的 wait 。因此,如果您以符合您的意图的代码结构来重写代码,它将在没有任何警告的情况下起作用:

val work = async { doSomeHardWork() } // start work
val result = withTimeoutOrNull(1, TimeUnit.SECONDS) { work.await() } // wait with timeout

如果您碰巧需要多次这种代码模式,则可以为自己定义一个方便的扩展名:

suspend fun <T> Deferred<T>.awaitWithTimeout(time: Long, unit: TimeUnit): T? =
    withTimeoutOrNull(time, unit) { await() }

然后编写甚至更好的代码来反映您的意图:

val result = async { doSomeHardWork() }.awaitWithTimeout(1, TimeUnit.SECONDS)

但是要小心。等待超时后,以async开头的这些协程将继续运行。除非您采取措施限制同时运行的后台作业的数量,否则这很容易导致资源泄漏。