Dans la kotlinx.coroutines
bibliothèque, vous pouvez démarrer une nouvelle coroutine en utilisant soit launch
(avec join
) ou async
(avec await
). Quelle est la différence entre eux?
Dans la kotlinx.coroutines
bibliothèque, vous pouvez démarrer une nouvelle coroutine en utilisant soit launch
(avec join
) ou async
(avec await
). Quelle est la différence entre eux?
Réponses:
launch
est utilisé pour tirer et oublier la coroutine . C'est comme démarrer un nouveau fil. Si le code à l'intérieur de se launch
termine avec une exception, il est traité comme une exception non interceptée dans un thread - généralement imprimé sur stderr dans les applications JVM principales et plante les applications Android. join
est utilisé pour attendre la fin de la coroutine lancée et il ne propage pas son exception. Cependant, une coroutine enfant en panne annule également son parent avec l'exception correspondante.
async
est utilisé pour démarrer une coroutine qui calcule un résultat . Le résultat est représenté par une instance de Deferred
et vous devez l' utiliser await
. Une exception non interceptée à l'intérieur du async
code est stockée dans le résultat Deferred
et n'est livrée nulle part ailleurs, elle sera supprimée silencieusement à moins qu'elle ne soit traitée. Vous NE DEVEZ PAS oublier la coroutine que vous avez commencée avec async .
Je trouve ce guide https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md utile. Je citerai les parties essentielles
🦄 coroutine
Les coroutines sont essentiellement des threads légers.
Vous pouvez donc considérer la coroutine comme quelque chose qui gère les threads de manière très efficace.
🐤 lancement
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
}
Alors launch
démarre un thread d'arrière-plan, fait quelque chose et renvoie un jeton immédiatement comme Job
. Vous pouvez appeler join
cela Job
pour bloquer jusqu'à ce que ce launch
fil soit terminé
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
}
🦆 asynchrone
Conceptuellement, async est comme le lancement. Il démarre une coroutine distincte qui est un thread léger qui fonctionne simultanément avec toutes les autres coroutines. La différence est que le lancement renvoie un Job et ne porte aucune valeur résultante, tandis que async renvoie un Deferred - un futur léger et non bloquant qui représente une promesse de fournir un résultat plus tard.
Alors async
démarre un thread d'arrière-plan, fait quelque chose et renvoie un jeton immédiatement comme 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")
}
Vous pouvez utiliser .await () sur une valeur différée pour obtenir son résultat final, mais Deferred est également un Job, vous pouvez donc l'annuler si nécessaire.
Alors Deferred
est en fait un Job
. Voir https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html
interface Deferred<out T> : Job (source)
🦋 async est impatient par défaut
Il existe une option de paresse pour asynchroniser à l'aide d'un paramètre de démarrage facultatif avec une valeur de CoroutineStart.LAZY. Il démarre la coroutine uniquement lorsque son résultat est requis par certains wait ou si une fonction de démarrage est appelée.
launch
et async
sont utilisés pour démarrer de nouvelles coroutines. Mais, ils les exécutent de manière différente.
Je voudrais montrer un exemple très basique qui vous aidera à comprendre la différence très facilement
- lancement
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;
}
}
Dans cet exemple, mon code télécharge 3 données en cliquant sur le btnCount
bouton et affiche pgBar
la barre de progression jusqu'à ce que tout le téléchargement soit terminé. Il y a 3 suspend
fonctions downloadTask1()
, downloadTask2()
et downloadTask3()
qui télécharge des données. Pour le simuler, j'ai utilisé delay()
dans ces fonctions. Ces fonctions attendent 5 seconds
, 8 seconds
et 5 seconds
respectivement.
Comme nous l'avons utilisé launch
pour démarrer ces fonctions de suspension, launch
les exécutera séquentiellement (une par une) . Cela signifie que, downloadTask2()
commencerait une fois downloadTask1()
terminé et downloadTask3()
ne commencerait qu'une fois downloadTask2()
terminé.
Comme dans la capture d'écran de sortie Toast
, le temps total d'exécution pour terminer les 3 téléchargements conduirait à 5 secondes + 8 secondes + 5 secondes = 18 secondes aveclaunch
- asynchrone
Comme nous l'avons vu, cela launch
permet l'exécution sequentially
des 3 tâches. Le temps était venu de terminer toutes les tâches 18 seconds
.
Si ces tâches sont indépendantes et si elles n'ont pas besoin du résultat de calcul d'une autre tâche, nous pouvons les faire fonctionner concurrently
. Ils commenceraient en même temps et fonctionneraient simultanément en arrière-plan. Cela peut être fait avec async
.
async
renvoie une instance de Deffered<T>
type, où T
est le type de données renvoyées par notre fonction de suspension. Par exemple,
downloadTask1()
retournerait Deferred<String>
car String est le type de retour de la fonctiondownloadTask2()
retournerait Deferred<Int>
car Int est le type de retour de la fonctiondownloadTask3()
retournerait Deferred<Float>
car Float est le type de retour de la fonctionNous pouvons utiliser l'objet de retour de async
de type Deferred<T>
pour obtenir la valeur retournée dans T
type. Cela peut être fait avec un await()
appel. Vérifiez le code ci-dessous par exemple
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 cette façon, nous avons lancé les 3 tâches simultanément. Donc, mon temps d'exécution total pour terminer serait seulement 8 seconds
ce qui est le temps downloadTask2()
car c'est la plus grande des 3 tâches. Vous pouvez le voir dans la capture d'écran suivante dansToast message
launch
c'est pour les funs séquentiels , tandis que async
pour les simultanés
launch
et async
vont démarrer de nouvelles coroutines. Vous comparez une seule coroutine sans enfants à une seule coroutine avec 3 enfants. Vous pouvez remplacer chacune des async
invocations par launch
et absolument rien ne changerait en ce qui concerne la concurrence.
les deux constructeurs de coroutine, à savoir le lancement et l'asynchrone, sont essentiellement des lambdas avec un récepteur de type CoroutineScope, ce qui signifie que leur bloc interne est compilé en tant que fonction de suspension, par conséquent, ils s'exécutent tous les deux en mode asynchrone ET ils exécuteront tous les deux leur bloc séquentiellement.
La différence entre lancer et asynchrone est qu'ils offrent deux possibilités différentes. Le générateur de lancement renvoie un Job mais la fonction async renverra un objet Deferred. Vous pouvez utiliser launch pour exécuter un bloc dont vous n'attendez pas de valeur renvoyée, c'est-à-dire écrire dans une base de données ou enregistrer un fichier ou traiter quelque chose qui est simplement appelé pour son effet secondaire. D'autre part, async qui retourne un Deferred comme je l'ai dit précédemment renvoie une valeur utile de l'exécution de son bloc, un objet qui enveloppe vos données, vous pouvez donc l'utiliser principalement pour son résultat mais éventuellement aussi pour son effet secondaire. NB: vous pouvez supprimer le différé et récupérer sa valeur à l'aide de la fonction wait, qui bloquera l'exécution de vos instructions jusqu'à ce qu'une valeur soit renvoyée ou qu'une exception soit levée!
les deux coroutine builder (lancement et asynchrone) sont annulables.
quoi de plus?: oui au lancement si une exception est levée dans son bloc, la coroutine est automatiquement annulée et les exceptions sont livrées. D'autre part, si cela se produit avec async, l'exception n'est pas propagée davantage et doit être interceptée / gérée dans l'objet Deferred renvoyé.
plus sur coroutines https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html https://www.codementor.io/blog/kotlin-coroutines-6n53p8cbn1
lancer renvoie un travail
async renvoie un résultat (tâche différée)
lancer avec join est utilisé pour attendre que le travail soit terminé.
async est utilisé pour calculer certains résultats. Il crée une coroutine et renvoie son résultat futur en tant qu'implémentation de Deferred. La coroutine en cours d'exécution est annulée lorsque le différé résultant est annulé.
Considérez une méthode asynchrone qui renvoie une valeur de chaîne. Si la méthode async est utilisée sans attendre, elle retournera une chaîne Deferred mais si await est utilisé, vous obtiendrez une chaîne comme résultat
La principale différence entre async et launch. Deferred renvoie une valeur particulière de type T après la fin de l'exécution de votre Coroutine, contrairement à Job.
Async vs Launch Async vs Launch Diff Image
lancer / asynchrone aucun résultat
asynchrone pour résultat