Bienvenido, me llamo Luis y aquí les traigo un post.
Índice
Otro operador asombroso, Flow
, de corrutinas
en Kotlin
Cuando llamamos a una función asincrónica como una llamada de servicio, recuperando datos de la base de datos, leyendo archivos o cualquier otra cosa.
Necesitamos una devolución de llamada para que sepamos que la operación ha finalizado y podemos reanudar el trabajo real como en los móviles actualizando la interfaz de usuario después de recibir datos de sus servidores.
Sí, está bien, pero cuando esté en tiempo real, no será tan fácil.
Ahí es donde entra en juego el poder real de las corrutinas, ya que con las corrutinas ya no es necesario escribir devoluciones de llamada para funciones asincrónicas.
Existe un concepto asombroso llamado funciones de suspensión en corrutinas que hace todo el trabajo que necesita y devuelve los datos en el tipo de datos esperado.
suspend fun getFilteredItems(token : String){ listItems = getItems(token) filteredItems = getFilteredItems(listItems) update() } suspend fun getItems( ... ) suspend fun getFilteredItems( ... ) fun update()
Muestra de Coroutine simple.
Esto salvará a los desarrolladores del infierno de las devoluciones de llamada y se concentrará en su lógica empresarial en el formato normal en el que están trabajando. ¿No es genial? Esta es solo una descripción general de las corrutinas.
Con las corrutinas, puede ejecutar de forma asincrónica, pero secuencial.
Consulte los siguientes enlaces para conocer las corrutinas en Kotlin , antes de continuar en este artículo.
Suspender funciones
Una función de suspensión es simplemente una función que se puede pausar y reanudar en un momento posterior.
Pueden ejecutar una operación de larga duración y esperar a que se complete sin bloquear llamadas de servicio, operaciones de base de datos o leer un archivo largo.
La sintaxis de una función de suspensión es similar a la de una función regular excepto por la adición de la suspend
palabra clave al principio.
Lo maravilloso de una función de suspensión es que puede devolver cualquier número de respuestas.
suspend fun getItem() : Response suspend fun getItems() : List<Response>
Veamos cómo funciona esto.
Por ejemplo, tenemos una función de suspensión foo
que devuelve un conjunto de respuestas como se muestra a continuación.
suspend fun foo() : List<Responses> = buildlist{ add(Execute("A")) add(Execute("B")) add(Execute("C")) }
Luego, lo llama desde su flujo como se muestra a continuación.
fun getData() { val list = withContext(Dispatchers.IO) { foo() } for (x in list) println(x) }
Lo que sucede aquí es que cuando llamas a la función de suspensión foo
, comienza a ejecutarse una por una y después de completar todas las ejecuciones, devolverá la lista de respuestas.
Entonces, aquí tenemos que esperar hasta que finalicen todas las ejecuciones solicitadas, lo cual no es una solución optimizada.
Podríamos hacerlo mejor, por lo que la siguiente solución que encontró el equipo de Kotlin fue Channel.
Canal
Los canales son tuberías estructuradas, en las que envía datos por un lado y recibe datos por el otro, como se muestra a continuación.
Para usar canales, debe cambiar un poco de código, en lugar de usar List<Response>
como tipo de retorno, usamos ReceiveChannel<Response>
, y en lugar de buildlist
usar produce
, y en lugar de addTo
reemplazarlo con send
.
Echar un vistazo:
suspend fun foo() : ReceiveChannel<Response> = produce{ send(execute("A")) send(xecute("B")) send(Execute("C")) }
Y luego, mientras lo usamos en nuestro flujo, recibiremos canales en lugar de una lista de respuestas. Luego, itera e imprime, eche un vistazo:
fun getData() { val chaannel = withContext(Dispatchers.IO) { foo() } for (x in channel) println(x) }
¿Qué diferencia hace? Veamos.
Cuando llame a la función foo
, creará un canal y lo devolverá inmediatamente, pero no iniciará la ejecución. Ahora tenemos dos corrutinas que se están ejecutando. Uno para emitir los datos y otros para observarlo.
Cuando llama al canal mientras itera, la ejecución comienza y ejecutará el primero y devolverá la respuesta y luego el segundo y otros de manera similar, eche un vistazo para tener una mejor idea:
Por lo tanto, al utilizar canales, ya no es necesario esperar para completar todas las ejecuciones. Pero hay un problema aquí, los canales están calientes.
Recuerda que dije antes que se están ejecutando dos corrutinas, una para observar y otra para emitir. ¿Y si no hay observador, ya sea por error o por alguna excepción?
Sabes que los canales son como abrir una conexión de red o leer un archivo que usa recursos costosos y si no hay un observador, la conexión permanecerá abierta, buscando al observador.
Podemos resolver este tipo de cosas, pero a la larga, no será optimista. Causará serios problemas de depuración y prueba a largo plazo.
Podemos hacerlo mejor, ¿verdad? Y así es como salió a la luz el concepto Flow de Kotlin.
Flujo de Kotlin
La versión estable de Flow se lanzó hace unos días. Flow no solo resuelve los puntos débiles de los canales, sino que también ofrece muchas funciones nuevas que verá en este artículo.
Para usar Flow en el ejemplo anterior, solo necesitamos cambiar algunas cosas como el tipo de retorno a Flow
y el usuario en flow
lugar de produce
. Y dentro del flujo, tienes que usar emit
, echar un vistazo:
fun foo() : Flow<Response> = flow{ emit(execute("A")) emit(execute("B")) emit(execute("C")) }
Por otro lado, en su función general, debe usar recopilar en el resultado de foo
. Estamos recolectando todos los elementos emitidos por el flujo foo
.
fun getData() { val flowData = foo() withContext(Dispatchers.IO) { flowdata.collect{ println(x) } }
Lo que sucede aquí es que cuando llama foo
, devuelve inmediatamente el objeto de flujo pero no comienza a ejecutarse.
Cuando comienza a recopilar el flujo, comienza a ejecutarse y se ejecuta una solicitud. Devuelve el resultado y luego comienza el siguiente hasta que no quede más.
Es similar a Channel, emite datos y recibe hasta que no hay más, pero la gran diferencia es que el flujo es frío. Incluso si no hay un observador, ya sea por error o intencionalmente, la corrutina no lo mantendrá porque no inició nada.
Flow le da un comportamiento reactivo a su funcionalidad al emitir el resultado después de completar la ejecución actual, a diferencia de esperar para completar toda la lista de solicitudes.
El flujo es declarativo
Cuando llamamos a la función foo
en el ejemplo flow
, lo que sucede es que crea un flujo y lo devuelve, por lo que podemos usar algunos operadores como map
para hacer que el flujo sea más declarativo, como se muestra a continuación.
fun foo() : Flow<Response> = flowOf("A","B","C").map{ name-> execute(name) } }
Aquí, si observa, la función foo
no es una función de suspensión. ¿Por qué?
Como dije anteriormente, solo define el objeto de flujo, luego lo emite inmediatamente y solo calcula o se ejecuta cuando ha comenzado a recopilar.
El flujo es reactivo
Al leer este titular, muchos de los desarrolladores piensan en RxJava
. Sí, RxJava
es una especie de inicio de un programa reactivo en JVM
donde Kotlin
se ejecuta principalmente.
Echemos un vistazo de cerca, por ejemplo, si desea transformar cualquier objeto de tipo A en tipo B en RxJava
, tenemos un operador llamado map
.
flatmapSingle
.Pero en Kotlin, tenemos un operador llamado map
, la transformación lambda de este operador es el modificador de suspensión que lo hace utilizable map
tanto en trabajos sincrónicos como asincrónicos.
Podemos evitar cientos de operadores como este usando Kotlin porque Kotlin tiene funciones de suspensión donde RxJava
y otros no, porque no están desarrolladas en Kotlin.
Antes de terminar, todos los conceptos que se mencionan y explican en este artículo provienen de una charla de Roman Elizarov en KotlinConfig 2019
. Si está interesado, mire desde el siguiente enlace.
Gracias por leer este post.
Añadir comentario