Saltar al contenido

¿Cuál es la diferencia entre iniciar / unirse y asíncrono / esperar en las corrutinas de Kotlin?

Ten en cuenta que en las ciencias informáticas cualquier problema casi siempere suele tener varias soluciones, no obstante mostraremos lo más óptimo y mejor.

Solución:

  • launch se usa para dispara y olvídate de la rutina. Es como comenzar un nuevo hilo. Si el código dentro del launch termina con excepción, entonces se trata como no atrapado excepción en un hilo: generalmente se imprime en stderr en aplicaciones JVM de backend y bloquea las aplicaciones de Android. join se utiliza para esperar a que se complete la corrutina iniciada y no propaga su excepción. Sin embargo, se estrelló niño coroutine también cancela su padre con la excepción correspondiente.

  • async se usa para iniciar una corrutina que calcule algún resultado. El resultado está representado por una instancia de Deferred y tu debe usar await en eso. Una excepción no detectada dentro del async el 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 corrutina que comenzó con async.

Encuentro que esta guía https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md es útil. Citaré las partes esenciales

corrutina

Esencialmente, las corrutinas son hilos livianos.

Por lo tanto, puede pensar en la corrutina como algo que gestiona el hilo de una manera muy eficiente.

lanzamiento

fun main(args: Array) 
    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 inicia un hilo en segundo plano, hace algo y devuelve un token inmediatamente como Job. Puedes llamar join en este Job para bloquear hasta este launch hilo completo

fun main(args: Array) = runBlocking 
    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

asincrónico

Conceptualmente, async es como un lanzamiento. Inicia una corrutina separada que es un hilo liviano que funciona simultáneamente con todas las demás corrutinas. La diferencia es que el lanzamiento devuelve un trabajo y no tiene ningún valor resultante, mientras que async devuelve un diferido: un futuro liviano sin bloqueo que representa una promesa de proporcionar un resultado más adelante.

Entonces async inicia un hilo en segundo plano, hace algo y devuelve un token inmediatamente como Deferred.

fun main(args: Array) = runBlocking 
    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 Deferred también es un trabajo, por lo que puede cancelarlo si es necesario.

Entonces Deferred es en realidad un Job. Consulte https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html

interface Deferred : Job (source)

async está ansioso por defecto

Existe una opción de pereza para realizar la sincronización mediante un parámetro de inicio opcional con un valor de CoroutineStart.LAZY. Inicia una rutina solo cuando su resultado es necesario para alguien que espera o si se invoca una función de inicio.

launch y async se utilizan para iniciar nuevas corrutinas. Pero, los ejecutan de manera diferente.

Me gustaría mostrar un ejemplo muy básico que le ayudará a entender la diferencia muy fácilmente.

  1. 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 btnCount botón y mostrando pgBar barra de progreso hasta que se complete toda la descarga. Hay 3 suspend funciones downloadTask1(), downloadTask2() y downloadTask3() que descarga datos. Para simularlo, he usado delay() en estas funciones. Estas funciones esperan 5 seconds, 8 seconds y 5 seconds respectivamente.

Como hemos usado launch para iniciar estas funciones de suspensión, launch los ejecutará secuencialmente (uno por uno). Esto significa que, downloadTask2() comenzaría después downloadTask1() se completa y downloadTask3() comenzaría solo después de downloadTask2() se completa.

Como en la captura de pantalla de salida Toast, el tiempo de ejecución total para completar las 3 descargas conduciría a 5 segundos + 8 segundos + 5 segundos = 18 segundos con launch

Ejemplo de lanzamiento

  1. asincrónico

Como vimos eso launch hace ejecución sequentially para las 3 tareas. El tiempo para completar todas las tareas fue 18 seconds.

Si esas tareas son independientes y si 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 tipo, donde T es el tipo de datos que devuelve nuestra función de suspensión. Por ejemplo,

  • downloadTask1() volvería Deferred como String es el tipo de función de retorno
  • downloadTask2() volvería Deferred como Int es el tipo de función de retorno
  • downloadTask3() volvería Deferred como Float es el tipo de función de retorno

Podemos usar el objeto de retorno de async de tipo Deferred para obtener el valor devuelto en T escribe. Eso se puede hacer con await() llama. Verifique el código a continuación, 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 cual es el momento para downloadTask2() ya que es la más grande de las 3 tareas. Puede ver esto en la siguiente captura de pantalla en Toast message

esperar ejemplo

Si aceptas, eres capaz de dejar una división acerca de qué le añadirías a esta reseña.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *