Kotlin Coroutines 101 — Jobs
En este artículo estaré vamos a mencionar algunos aspectos que encuentro interesantes sobre los Jobs.
¿Qué es un Job?
En el primer artículo que escribí tuvimos ocasión de lanzar una corutina mediante la acción de un botón. Pero ¿qué pasa si por algún motivo queremos verificar el estado de la corutina o cancelarla, si fuera necesario?. Es aquí donde entra a tallar la tarea de un Job.
Un Job es una representación de una corutina y nos va a permitir realizar algunas acciones sobre ella.
¿Cuáles son sus estados?
Los Jobs cuentan con estados, los cuales listaré a continuación:
- New : representa el estado de creación de una corutina y la asociación de un Job a ella.
- Active : este estado representa el momento en el que se inicia ejecución y mantiene la ejecución de una corutina.
- Completing : este estado representa el momento en que la corutina ha finalizado su ejecución y precisa esperar a que otras corutinas asociadas a ella o en una relación de padre-hijo finalicen.
- Completed : este estado representa el estado final de una corutina, el momento en el que toda su ejecución, incluyendo las corutinas hijas, ha finalizado.
- Cancelling : este estado representa el momento en el que se ha cancelado la corutina y se espera la cancelación de los Jobs asociados a ella.
- Cancelled : este es el estado final una vez que se ha procedido a la cancelación total o completa.
Debido a la existencia de estos estados, es posible mediante un Job acceder a las siguientes propiedades:
- isActive : esta propiedad nos permitirá saber si un Job se encuentra en estado activo o no.
- isCancelled : esta propiedad nos permitirá saber si un Job ha sido cancelado o no.
- isCompleted : esta propiedad nos permitirá saber si un Job ha finalizado o ha completado todas sus operaciones.
¿Cuándo emplear estas propiedades? La respuesta sencilla sería emplearlas en momentos en los que la ejecución o lógica de tu corutina demande saber si debe continuar o no si el estado de la misma cambia.
La función invokeOnCompletion
Un aspecto interesante de los Jobs es que es posible ejecutar código una vez que estos finalizan y esto es gracias a la función invokeOnCompletion(), esta función recibe como parámetro un objeto nullable de tipo Throwable? que nos permitirá validar la causa por la que una corutina fue cancelada.
job1.invokeOnCompletion { throwable ->
log("Complete my job 1")
throwable?.let {
log("There is a throwable object!")
when ( it ) {
is CancellationException -> {
log( it.message ?: "Without message")
}
}
}
}
¿Cómo cancelar un job?
Cancelar un Job se puede conseguir mediante la invocación de su función cancel(). Sin embargo, te va a pedir que pases una instancia de la clase CancellationException, preferiblemente hay que agregarle un mensaje que indique la causa por la que se está cancelando el Job.
¿Cómo concatenar jobs?
Concatenar Jobs es relativamente sencillo, para ello requerimos ejecutar la función join(). El comportamiento por defecto de la función join() implica que la corutina que lo invoque se suspenda hasta que este Job esté completo. Sin embargo, si al builder de turno le pasamos el parámetro start=CoroutineStart.LAZY lo que acontecerá es que dicho Job pasará a un estado activo en el punto en el que join() sea invocado.
¿Cómo definir una relación de padre/hijo entre Jobs?
Definir una relación de padre e hijos es posible si pasas el Job como parte del contexto de la corutina que quieres definir como Job hijo.
val job1 = coroutineScope.launch(start = CoroutineStart.LAZY,
context = handlerException) {...}
val childCoroutineScope = CoroutineScope(Dispatchers.Main + job1)
val job2 = childCoroutineScope.launch{...}
¿Cómo manejar una excepción en un job?
Para manejar excepciones tienes dos forma; la primera, de forma tradicional usando un try/catch en la sección de código que quieres asegurar; la otra, es declarando una instancia de CoroutineExceptionHandler, el cual recibe como parámetros tanto un objeto conteniendo información sobre el contexto de la corutina y un objeto del tipo Throwable. Este objeto debe pasarse como parte del contexto de la corutina.
val handlerException = CoroutineExceptionHandler {
coroutineContext, throwable ->
run {
log("$coroutineContext - ${throwable.message}")
}
}
val job1 = coroutineScope.launch(context = handlerException ) {...}
Para complementar les dejo un video para complementar el contenido de este artículo, regálenme un Me gusta si les es de utilidad y un comentario en el video.
Top comments (0)