Digamos que una solicitud inició un cálculo largo, pero está lista para esperar no más de X segundos por el resultado. Pero en lugar de interrumpir el cálculo, me gustaría que continuara en paralelo hasta completarlo.
La primera condición ("esperar no más de") se cumple con la función withTimeoutOrNull .
¿Cuál es la forma estándar (idiomática Kotlin) de continuar el cálculo y ejecutar alguna acción final cuando el resultado está listo?
Ejemplo :
fun longCalculation(key: Int): String { /* 2..10 seconds */} // ---------------------------- cache = Cache<Int, String>() suspend fun getValue(key: Int) = coroutineScope { val value: String? = softTimeoutOrNull( timeout = 5.seconds(), calculation = { longCalculation() }, finalAction = { v -> cache.put(key, v) } ) // return calculated value or null }Este es un caso lo suficientemente especializado que no creo que haya un consenso sobre una forma idiomática de hacerlo.
Dado que desea que el trabajo continúe en segundo plano, incluso si se reanuda la corrutina actual, necesita un CoroutineScope separado para iniciar ese trabajo en segundo plano, en lugar de usar el generador coroutineScope que inicia la corrutina como una corrutina secundaria de la actual. Ese alcance externo determinará la vida útil de las rutinas que lanza (cancelándolas si se cancela). Por lo general, si usa corrutinas, ya tiene un alcance a mano que está asociado con el ciclo de vida de la clase actual.
Creo que esto haría lo que estás describiendo. La corrutina actual puede envolver una llamada Deferred.await() con withTimeoutOrNull para ver si puede esperar a la otra corrutina (esa no es una corrutina secundaria ya que se lanzó directamente desde un CoroutineScope externo) sin interferir con ella.
suspend fun getValue(key: Int): String? { val deferred = someScope.async { longCalculation(key) .also { cache.put(key, it) } } return withTimeoutOrNull(5000) { deferred.await() } }Aquí hay una versión generalizada:
/** * Launches a coroutine in the specified [scope] to perform the given [calculation] * and [finalAction] with that calculation's result. Returns the result of the * calculation if it is available within [timeout]. */ suspend fun <T> softTimeoutOrNull( scope: CoroutineScope, timeout: Duration, calculation: suspend () -> T, finalAction: suspend (T) -> Unit = { } ): T? { val deferred = scope.async { calculation().also { finalAction(it) } } return withTimeoutOrNull(timeout) { deferred.await() } }