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 dellaunch
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 deDeferred
y tu debe usarawait
en eso. Una excepción no detectada dentro delasync
el código se almacena dentro del resultadoDeferred
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.
- 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
- 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íaDeferred
como String es el tipo de función de retornodownloadTask2()
volveríaDeferred
como Int es el tipo de función de retornodownloadTask3()
volveríaDeferred
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
Si aceptas, eres capaz de dejar una división acerca de qué le añadirías a esta reseña.