Kotlin Coroutines 101 — Deferred

…y el uso de async/await

Armando Picón
DevPicon by Armando
3 min readMay 18, 2020

--

En este artículo vamos a mencionar algunos aspectos que encuentro interesantes sobre los Deferred.

Unas notas que tenía sobre el tema

¿Qué es un objeto Deferred?

En un artículo anterior hablamos sobre los Jobs y de lo que podíamos hacer con ello.

El tipo Deferred extiende de Job, por lo tanto heredará todo el comportamiento y los estados ; adicionalmente, tendrá la capacidad de retornar un resultado diferido (por eso el nombre de Deferred) como producto de la ejecución de una corutina iniciada mediante el uso de la función async.

“it is a Job with a result”. (según la documentación oficial)

¿Un Job con resultado? Ejecución de Async/Await

Para comenzar a hablar sobre esta característica es necesario explorar el builder async y reforzar su diferencia con el builder launch que vimos en artículos anteriores.

Cuando hacemos uso del builder launch su implementación luce más o menos como se muestra a continuación:

val coroutineScope = CoroutineScope(Dispatchers.Main)
val job1:Job = coroutineScope.launch {...}

Como podemos observar el lanzar esta corutina nos permite obtener un Job el cual nos permitirá evaluar estados o cancelar la corutina. Mas no nos permitirá hacer otra cosa más allá de ejecutar el bloque de código que contenga.

Por otro lado, tenemos el builder async cuya implementación luce así:

val coroutineScope = CoroutineScope(Dispatchers.Main)
val deferred1: Deferred<String> = coroutineScope.async {
// Aquí va el bloque de código
"..." //<-- el valor resultante que contendrá el Deferred
}
val deferred2 = coroutineScope.async {...}
//...coroutineScope.launch{ processResult(deferred1.await(), deferred2.await())}

Hay algunas cosas que debemos tomar en cuenta respecto al uso de los async:

  • Debemos considerar que la última línea del bloque de código será tomada como el valor resultante que será contenido en nuestro objeto diferido.
  • Para obtener el valor generado como resultado, debemos llamar a la función suspendida await(), al generar un punto de suspensión, además de evitar el bloqueo del hilo en el que se esté ejecutando, si la operación de la corutina no ha terminado, la va a esperar hasta que retorne un resultado.
  • A diferencia del launch cuyas invocaciones son secuenciales, el uso de async y await nos permite generar una sensación de paralelismo al permitirnos lanzar varias operaciones corriendo al mismo tiempo por lo que la mejor forma de aprovecharlos consta de invocar a más de una operación no dependientes entre sí cuyos resultados puedan coincidir en la invocación de alguna operación adicional (por eso en el código de ejemplo estoy colocando dos corutinas en lugar de una sola). En esto consiste el concepto de Parallel decomposition o Descomposición paralela si lo quisieramos traducir.
  • Si recuerdan en el artículo sobre Jobs hablamos del efecto que tenía la invocación de la función suspendida join(), en este punto await() actúa de forma similar con la evidente diferencia de que sí retornamos un valor en este caso.

Consideraciones adicionales

Es importante considerar que la cancelación o la ocurrencia de una excepción en el interior de una corutina provocará la cancelación de la corutina padre así como de aquellas que estén asociadas a ella debido a que su ejecución estará dentro de un mismo ámbito o scope lo cual es parte del concepto de Structured concurrency paradigm o Paradigma de concurrencia estructurada en español.

--

--

Armando Picón
DevPicon by Armando

lifelong learner • 👨🏽‍💻 Android eng (ex: @uber ) • 🇵🇪 @ 🇨🇱 • @gdgopen • content creator @devpicon | @primosauda