Na kotlinx.coroutines
biblioteca, você pode iniciar uma nova rotina usando launch
(com join
) ou async
(com await
). Qual a diferença entre eles?
Na kotlinx.coroutines
biblioteca, você pode iniciar uma nova rotina usando launch
(com join
) ou async
(com await
). Qual a diferença entre eles?
Respostas:
launch
é usado para disparar e esquecer a rotina . É como iniciar um novo tópico. Se o código dentro do launch
finalizar com exceção, ele será tratado como exceção não capturada em um encadeamento - geralmente impresso para stderr nos aplicativos JVM de back-end e trava os aplicativos Android. join
é usado para aguardar a conclusão da corotina lançada e não propaga sua exceção. No entanto, uma rotina infantil rotineira travada também cancela seu pai com a exceção correspondente.
async
é usado para iniciar uma rotina que calcula algum resultado . O resultado é representado por uma instância de Deferred
e você deve usá await
-lo. Uma exceção não capturada dentro do async
código é armazenada dentro do resultado Deferred
e não é entregue em nenhum outro lugar; ela será eliminada silenciosamente, a menos que seja processada. Você NÃO DEVE esquecer a corotina que você iniciou com a assíncrona .
Acho que este guia https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md é útil. Vou citar as partes essenciais
🦄 corotina
Essencialmente, as corotinas são fios leves.
Assim, você pode pensar na corotina como algo que gerencia o encadeamento de maneira muito eficiente.
🐤 lançamento
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
}
Então launch
inicia um thread em segundo plano, faz alguma coisa e retorna um token imediatamente como Job
. Você pode chamar join
isso Job
para bloquear até que este launch
segmento seja concluído
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
}
🦆 assíncrono
Conceitualmente, assíncrono é como o lançamento. Inicia uma corotina separada, que é um encadeamento leve que funciona simultaneamente com todas as outras corotinas. A diferença é que o lançamento retorna um trabalho e não carrega nenhum valor resultante, enquanto o async retorna um adiado - um futuro leve e sem bloqueio que representa uma promessa de fornecer um resultado posteriormente.
Então async
inicia um thread em segundo plano, faz alguma coisa e retorna um token imediatamente 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")
}
Você pode usar .await () em um valor adiado para obter seu resultado final, mas adiado também é um trabalho, para que você possa cancelá-lo, se necessário.
Então Deferred
é realmente um Job
. Consulte https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html
interface Deferred<out T> : Job (source)
🦋 assíncrono está ansioso por padrão
Há uma opção de preguiça para sincronizar usando um parâmetro de início opcional com o valor CoroutineStart.LAZY. Ele inicia a rotina somente quando seu resultado é necessário por alguns aguardam ou se uma função de início é chamada.
launch
e async
são usados para iniciar novas corotinas. Mas, eles os executam de maneira diferente.
Eu gostaria de mostrar um exemplo muito básico que o ajudará a entender a diferença com muita facilidade
- lançamento
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;
}
}
Neste exemplo, meu código está baixando 3 dados ao clicar no btnCount
botão e mostrando a pgBar
barra de progresso até que todo o download seja concluído. Existem 3 suspend
funções downloadTask1()
, downloadTask2()
e downloadTask3()
que faz download de dados. Para simular, usei delay()
essas funções. Essas funções aguardam e 5 seconds
, respectivamente.8 seconds
5 seconds
Como usamos launch
para iniciar essas funções de suspensão, as launch
executamos sequencialmente (uma a uma) . Isso significa que, downloadTask2()
iniciaria após a downloadTask1()
conclusão e downloadTask3()
só seria iniciado após a downloadTask2()
conclusão.
Como na captura de tela de saída Toast
, o tempo total de execução para concluir todos os 3 downloads levaria a 5 segundos + 8 segundos + 5 segundos = 18 segundos comlaunch
- assíncrono
Como vimos, isso launch
faz a execução sequentially
de todas as 3 tarefas. O tempo para concluir todas as tarefas era 18 seconds
.
Se essas tarefas forem independentes e se não precisarem do resultado da computação de outras tarefas, podemos executá-las concurrently
. Eles começariam ao mesmo tempo e executariam simultaneamente em segundo plano. Isso pode ser feito com async
.
async
retorna uma instância do Deffered<T>
tipo, onde T
são os tipos de dados que nossa função de suspensão retorna. Por exemplo,
downloadTask1()
retornaria Deferred<String>
como String é o tipo de função de retornodownloadTask2()
retornaria Deferred<Int>
como Int é o tipo de função de retornodownloadTask3()
retornaria Deferred<Float>
como Float é o tipo de função de retornoPodemos usar o objeto de retorno de async
do tipo Deferred<T>
para obter o valor retornado no T
tipo. Isso pode ser feito com a await()
chamada. Verifique o código abaixo, por exemplo
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
}
Dessa forma, lançamos as três tarefas simultaneamente. Portanto, meu tempo total de execução seria apenas o 8 seconds
tempo necessário downloadTask2()
, pois é o maior de todas as 3 tarefas. Você pode ver isso na seguinte captura de tela emToast message
launch
é para async
launch
e async
iniciarão novas corotinas. Você está comparando uma única corotina sem filhos com uma única corotina com três filhos. Você pode substituir cada uma das async
invocações launch
e absolutamente nada mudaria em relação à simultaneidade.
os dois construtores de corotina, nomeadamente launch e async, são basicamente lambdas com um receptor do tipo CoroutineScope, o que significa que seu bloco interno é compilado como uma função de suspensão, portanto, ambos são executados em modo assíncrono E ambos executam seu bloco sequencialmente.
A diferença entre o lançamento e o assíncrono é que eles permitem duas possibilidades diferentes. O construtor de lançamento retorna um trabalho, no entanto, a função assíncrona retornará um objeto adiado. Você pode usar o launch para executar um bloco que não espera nenhum valor retornado, por exemplo, gravar em um banco de dados ou salvar um arquivo ou processar algo basicamente chamado apenas de seu efeito colateral. Por outro lado, a assíncrona que retorna um adiado como afirmei anteriormente retorna um valor útil da execução de seu bloco, um objeto que agrupa seus dados, para que você possa usá-lo principalmente como resultado, mas possivelmente também como efeito colateral. Nota: você pode retirar o diferido e obter seu valor usando a função aguardar, que bloqueará a execução de suas instruções até que um valor seja retornado ou exceções sejam lançadas!
o construtor de corotina (inicialização e async) é cancelável.
mais alguma coisa ?: sim com o lançamento, se uma exceção for lançada dentro de seu bloco, a corotina é automaticamente cancelada e as exceções são entregues. Por outro lado, se isso acontecer com a assíncrona, a exceção não será propagada mais e deverá ser capturada / manipulada dentro do objeto Adiado retornado.
mais sobre coroutines https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html https://www.codementor.io/blog/kotlin-coroutines-6n53p8cbn1
lançamento retorna um trabalho
async retorna um resultado (trabalho adiado)
O lançamento com junção é usado para aguardar a conclusão do trabalho. ele simplesmente suspende a corotina chamando join (), deixando o encadeamento atual livre para realizar outro trabalho (como executar outra corotina) enquanto isso.
assíncrono é usado para calcular alguns resultados. Ele cria uma rotina e retorna seu resultado futuro como uma implementação do Adiado. A corrotina em execução é cancelada quando o diferido resultante é cancelado.
Considere um método assíncrono que retorna um valor de sequência. Se o método assíncrono for usado sem aguardar, ele retornará uma sequência adiada, mas se for aguardado, você obterá uma sequência como resultado
A principal diferença entre assíncrono e inicialização. Adiado retorna um valor específico do tipo T depois que a Coroutine termina a execução, enquanto Job não.
Async vs Launch Async vs Launch Diff Image
iniciar / assíncrono sem resultado
assíncrono para resultado