Sobes.tech
Middle

Quelles capacités Kotlin offre-t-il pour travailler avec des opérations asynchrones?

sobes.tech IA

Réponse de l'IA

Kotlin fournit des coroutines pour simplifier les opérations asynchrones, offrant des threads légers qui ne bloquent pas le thread principal.

Fonctionnalités clés:

  • Fonctions de suspension: Fonctions avec le mot-clé suspend, qui peuvent être suspendues et reprises. Elles ne bloquent pas le thread dans lequel elles s'exécutent.
    suspend fun fetchData(): String {
        delay(1000) // Simule une opération longue
        return "Données reçues"
    }
    
  • Constructeurs de coroutines: Fonctions utilisées pour lancer des coroutines:
    • launch: Lance une nouvelle coroutine et retourne un Job (non bloquant).
      GlobalScope.launch {
          println("Coroutine lancée")
      }
      
    • async: Lance une nouvelle coroutine et retourne un Deferred<T> (bloquant lors de l'appel à .await()).
      val deferred = GlobalScope.async {
          fetchData()
      }
      // ...
      val result = deferred.await() // Attend le résultat
      
    • runBlocking: Bloque le thread actuel jusqu'à ce que la coroutine se termine (utilisé dans les ponts entre code bloquant et non bloquant, par exemple dans les tests).
      runBlocking {
          val data = fetchData()
          println(data)
      }
      
  • Contexte de coroutine: Ensemble d'éléments définissant le comportement de la coroutine, y compris Dispatcher et Job.
    • Dispatcher: Définit le thread ou le pool de threads où la coroutine s'exécute.
  • Dispatchers: Objets intégrés pour gérer les threads:
    • Dispatchers.Default: Pool général de threads en arrière-plan.
    • Dispatchers.IO: Optimisé pour l'entrée/sortie (opérations disque, réseau).
    • Dispatchers.Main: Thread principal de l'UI (pour Android, JavaFX, etc.).
    • Dispatchers.Unconfined: Lance la coroutine dans le thread actuel, mais peut la reprendre dans n'importe quel thread après suspension.
    GlobalScope.launch(Dispatchers.IO) {
        // Exécuté dans un thread d'entrée/sortie
    }
    
  • Concurrence structurée: Principe selon lequel les coroutines ont un parent (CoroutineScope) et sont automatiquement annulées lors de l'annulation du parent. Empêche les fuites de ressources et simplifie la gestion du cycle de vie.
    // CoroutineScope gère automatiquement les coroutines enfants
    val scope = CoroutineScope(Dispatchers.Default)
    val job = scope.launch {
        // ...
    }
    // Lors de l'annulation du scope, toutes les coroutines enfants sont annulées
    scope.cancel()
    
  • Canaux: Pour le transfert sécurisé de données entre coroutines. Peuvent être:
    • Sans tampon: La réception ne se produit qu'après l'envoi.
    • Bâtonné: Permet d'envoyer plusieurs éléments avant de bloquer.
    • Conflated: Ne stocke que le dernier élément envoyé.
    val channel = Channel<Int>()
    
    scope.launch {
        for (x in 1..5) channel.send(x)
        channel.close()
    }
    
    scope.launch {
        for (y in channel) println(y)
    }
    
  • Flow: Flux de données pouvant émettre plusieurs valeurs de manière asynchrone. Semblable à RxJava Observable, mais plus léger et intégré avec les coroutines.
    fun simpleFlow(): Flow<Int> = flow {
        for (i in 1..3) {
            delay(100)
            emit(i) // Émission de valeur
        }
    }
    
    runBlocking {
        simpleFlow().collect { value -> println(value) } // Consommation des valeurs
    }
    

Ces fonctionnalités permettent d'écrire du code asynchrone de style impératif, le rendant plus lisible et plus facile à maintenir par rapport à l'utilisation de callbacks ou de Future.