Как разрешить withTimeoutOrNull возвращать ноль, но завершать код в блоке
Мне нужен мой код для запуска блока и возврата значения через 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 ответ
IDE выдает предупреждение в этом случае, потому что async(ctx) { ... }.await()
обычно это ошибка и withContext(ctx) { ... }
обычно лучше отражает первоначальные намерения автора кода.
В случае вашего кода ваше намерение другое. Ваше намерение состоит в том, чтобы ждать в течение 1 секунды, не ограничивая doSomeHardWork
код. Однако структура вашего кода не отражает ваших намерений. Вы завернули весь блок в withTimeout
и положи doSomeHardWork
внутри, в то время как ваше намерение состояло лишь в том, чтобы сделать это в течение ограниченного времени. Итак, если вы переписываете свой код так, чтобы его структура соответствовала вашим намерениям, он будет работать без каких-либо предупреждений:
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
будет продолжать работать после истечения времени ожидания. Это может легко привести к утечке ресурсов, если вы не предпримете шаги по ограничению количества одновременно работающих фоновых заданий.