En la kotlinx.coroutines
biblioteca, puede comenzar una nueva rutina utilizando launch
(con join
) o async
(con await
). ¿Cuál es la diferencia entre ellos?
En la kotlinx.coroutines
biblioteca, puede comenzar una nueva rutina utilizando launch
(con join
) o async
(con await
). ¿Cuál es la diferencia entre ellos?
Respuestas:
launch
Se utiliza para disparar y olvidar la corutina . Es como comenzar un nuevo hilo. Si el código dentro del launch
termina con excepción, entonces se trata como una excepción no capturada en un hilo, generalmente impreso en stderr en aplicaciones JVM de back-end y bloquea las aplicaciones de Android. join
se utiliza para esperar la finalización de la rutina lanzada y no propaga su excepción. Sin embargo, una rutina infantil caída también cancela a su padre con la excepción correspondiente.
async
se usa para iniciar una rutina que calcula algún resultado . El resultado está representado por una instancia de Deferred
y debe usarlo await
. Una excepción no detectada dentro del async
código se almacena dentro del resultado Deferred
y no se entrega en ningún otro lugar, se eliminará silenciosamente a menos que se procese. NO DEBE olvidarse de la rutina que comenzó con async .
Encuentro útil esta guía https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md . Citaré las partes esenciales
🦄 corutina
Esencialmente, las corutinas son hilos livianos.
Por lo tanto, puede pensar en la rutina como algo que gestiona el hilo de una manera muy eficiente.
🐤 lanzamiento
fun main(args: Array<String>) {
launch { // launch new coroutine in background and continue
delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
println("World!") // print after delay
}
println("Hello,") // main thread continues while coroutine is delayed
Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}
Entonces launch
comienza un hilo de fondo, hace algo y devuelve un token inmediatamente como Job
. Puede invocar join
esto Job
para bloquear hasta que este launch
hilo se complete
fun main(args: Array<String>) = runBlocking<Unit> {
val job = launch { // launch new coroutine and keep a reference to its Job
delay(1000L)
println("World!")
}
println("Hello,")
job.join() // wait until child coroutine completes
}
🦆 asíncrono
Conceptualmente, async es como el lanzamiento. Comienza una rutina separada que es un hilo liviano que funciona simultáneamente con todas las otras rutinas. La diferencia es que el lanzamiento devuelve un Trabajo y no tiene ningún valor resultante, mientras que async devuelve un Diferido, un futuro ligero y sin bloqueo que representa una promesa de proporcionar un resultado más adelante.
Entonces async
comienza un hilo de fondo, hace algo y devuelve un token inmediatamente como Deferred
.
fun main(args: Array<String>) = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
}
Puede usar .await () en un valor diferido para obtener su resultado final, pero diferido también es un trabajo, por lo que puede cancelarlo si es necesario.
Entonces Deferred
es en realidad un Job
. Ver https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html
interface Deferred<out T> : Job (source)
🦋 async está ansioso por defecto
Hay una opción de pereza para sincronizar utilizando un parámetro de inicio opcional con un valor de CoroutineStart.LAZY. Inicia la rutina solo cuando alguien espera su resultado o si se invoca una función de inicio.
launch
y async
se utilizan para comenzar nuevas corutinas. Pero, los ejecutan de manera diferente.
Me gustaría mostrar un ejemplo muy básico que te ayudará a entender la diferencia muy fácilmente.
- lanzamiento
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnCount.setOnClickListener {
pgBar.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch {
val currentMillis = System.currentTimeMillis()
val retVal1 = downloadTask1()
val retVal2 = downloadTask2()
val retVal3 = downloadTask3()
Toast.makeText(applicationContext, "All tasks downloaded! ${retVal1}, ${retVal2}, ${retVal3} in ${(System.currentTimeMillis() - currentMillis)/1000} seconds", Toast.LENGTH_LONG).show();
pgBar.visibility = View.GONE
}
}
// Task 1 will take 5 seconds to complete download
private suspend fun downloadTask1() : String {
kotlinx.coroutines.delay(5000);
return "Complete";
}
// Task 1 will take 8 seconds to complete download
private suspend fun downloadTask2() : Int {
kotlinx.coroutines.delay(8000);
return 100;
}
// Task 1 will take 5 seconds to complete download
private suspend fun downloadTask3() : Float {
kotlinx.coroutines.delay(5000);
return 4.0f;
}
}
En este ejemplo, mi código está descargando 3 datos al hacer clic en el btnCount
botón y mostrando pgBar
la barra de progreso hasta que se complete toda la descarga. Hay 3 suspend
funciones downloadTask1()
, downloadTask2()
y downloadTask3()
que descarga datos. Para simularlo, lo he usado delay()
en estas funciones. Estas funciones espera a 5 seconds
, 8 seconds
y 5 seconds
respectivamente.
Como hemos usado launch
para iniciar estas funciones de suspensión, las launch
ejecutaremos secuencialmente (una por una) . Esto significa que downloadTask2()
comenzaría después de downloadTask1()
completarse y downloadTask3()
comenzaría solo después de downloadTask2()
completarse.
Como en la captura de pantalla de salida Toast
, el tiempo total de ejecución para completar las 3 descargas llevaría a 5 segundos + 8 segundos + 5 segundos = 18 segundos conlaunch
- asíncrono
Como vimos, eso launch
hace que la ejecución sequentially
de las 3 tareas. El momento para completar todas las tareas fue 18 seconds
.
Si esas tareas son independientes y no necesitan el resultado de cálculo de otra tarea, podemos hacer que se ejecuten concurrently
. Comenzarían al mismo tiempo y se ejecutarían simultáneamente en segundo plano. Esto se puede hacer con async
.
async
devuelve una instancia de Deffered<T>
tipo, donde T
es el tipo de datos que devuelve nuestra función de suspensión. Por ejemplo,
downloadTask1()
volvería Deferred<String>
como String es el tipo de función de retornodownloadTask2()
volvería Deferred<Int>
como Int es el tipo de función de retornodownloadTask3()
regresaría Deferred<Float>
como Float es el tipo de función de retornoPodemos usar el objeto de devolución from async
of type Deferred<T>
para obtener el valor devuelto en T
type. Eso se puede hacer con await()
llamada. Verifique el siguiente código, por ejemplo
btnCount.setOnClickListener {
pgBar.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch {
val currentMillis = System.currentTimeMillis()
val retVal1 = async(Dispatchers.IO) { downloadTask1() }
val retVal2 = async(Dispatchers.IO) { downloadTask2() }
val retVal3 = async(Dispatchers.IO) { downloadTask3() }
Toast.makeText(applicationContext, "All tasks downloaded! ${retVal1.await()}, ${retVal2.await()}, ${retVal3.await()} in ${(System.currentTimeMillis() - currentMillis)/1000} seconds", Toast.LENGTH_LONG).show();
pgBar.visibility = View.GONE
}
De esta manera, hemos lanzado las 3 tareas al mismo tiempo. Entonces, mi tiempo total de ejecución para completar sería solo 8 seconds
el tiempo necesario, downloadTask2()
ya que es la mayor de las 3 tareas. Puedes ver esto en la siguiente captura de pantalla enToast message
launch
es para diversiones secuenciales , mientras que async
para concurrentes
launch
y async
comenzarán nuevas corutinas. Usted está comparando una única corutina sin hijos con una única con 3 niños. Puede reemplazar cada una de las async
invocaciones launch
y absolutamente nada cambiaría con respecto a la concurrencia.
ambos constructores de coroutine, es decir, launch y async, son básicamente lambdas con un receptor de tipo CoroutineScope, lo que significa que su bloque interno se compila como una función de suspensión, por lo tanto, ambos se ejecutan en modo asíncrono Y ambos ejecutarán su bloque secuencialmente.
La diferencia entre el lanzamiento y el asíncrono es que permiten dos posibilidades diferentes. El generador de inicio devuelve un trabajo, sin embargo, la función asíncrona devolverá un objeto diferido. Puede usar el lanzamiento para ejecutar un bloque del que no espera ningún valor devuelto, es decir, escribir en una base de datos o guardar un archivo o procesar algo que básicamente solo llama su efecto secundario. Por otro lado, async que devuelve un diferido como dije anteriormente devuelve un valor útil de la ejecución de su bloque, un objeto que envuelve sus datos, por lo que puede usarlo principalmente para su resultado, pero posiblemente también para su efecto secundario. NB: puede quitar el diferido y obtener su valor utilizando la función esperar, que bloqueará la ejecución de sus declaraciones hasta que se devuelva un valor o se generen excepciones.
Ambos creadores de rutina (lanzamiento y asíncrono) son cancelables.
¿algo más ?: sí con el lanzamiento si se lanza una excepción dentro de su bloque, la rutina se cancela automáticamente y se entregan las excepciones. Por otro lado, si eso sucede con asíncrono, la excepción no se propaga más y debe capturarse / manejarse dentro del objeto diferido devuelto.
más sobre las rutinas https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html https://www.codementor.io/blog/kotlin-coroutines-6n53p8cbn1
lanzamiento devuelve un trabajo
async devuelve un resultado (trabajo diferido)
launch with join se usa para esperar hasta que el trabajo termine. Simplemente suspende la rutina que llama a join (), dejando el hilo actual libre para realizar otro trabajo (como ejecutar otra rutina) mientras tanto.
async se usa para calcular algunos resultados. Crea una rutina y devuelve su resultado futuro como una implementación de Diferido. La rutina en ejecución se cancela cuando se cancela el aplazamiento resultante.
Considere un método asíncrono que devuelve un valor de cadena. Si el método asíncrono se usa sin esperar, devolverá una cadena diferida, pero si se usa esperar, obtendrá una cadena como resultado
La diferencia clave entre asíncrono y lanzamiento. Diferido devuelve un valor particular de tipo T después de que su Coroutine termina de ejecutarse, mientras que Job no lo hace.
Async vs Launch Async vs Launch Diff Image
lanzamiento / asíncrono sin resultado
asíncrono para el resultado