PromiseKit : la promesse d’une vie asynchrone meilleure

Dans l’article précédent nous avons vu comment faire de la programmation concurrente avec le framework GCD. Aujourd’hui nous allons nous intéresser à la notion de Promesse (Promise en anglais) qui permet de gérer la concurrence en montant en abstraction au travers de la librairie open-source PromiseKit.

Les Promesses

Une promesse, aussi appelé futur ou délai dans d’autre langages ou librairies, est un objet qui contiendra le résultat de la tâche asynchrone dans le futur. Dans le détail on emballe l’exécution de la tâche asynchrone dans un objet (une promesse) et au travers d’une API simple et « fluent » on sera notifier de l’accomplissement ou non de cette tâche. Cette abstraction permet d’écrire du code d’une manière plus séquentielle et donc plus lisible et compréhensible.

Avant de vous noyez encore plus dans les explications voici un petit example en GCD pour commender : :

func getAsyncPosts(url: String, completion: (data: NSData, error: NSError?) -> Void) {
  // Implementation
}

let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)

dispatch_async(queue) { 
  self.getAsyncPosts("http://swift-tuto.fr") { data, error in
    dispatch_async(dispatch_get_main_queue()) {
      if let error = error {
        print("error", error)
      }
      else {
        print("posts", data)
      }
    }
  }
}

Dans cet example nous tirons un peu le trait parce qu’en réalité nous n’aurions pas besoin de GCD mais il a juste pour vocation à illustrer l’utilisation des promesses. La fonction getAsyncPosts permet de récupérer l’ensemble des posts d’une url passée en paramètre. L’exécution se fait de manière asynchrone et il notifie l’appelant au travers d’un block de complétion.

Maintenant écrivons l’équivalent avec l’utilisation des promesses de PromiseKit :

func getPosts(url: String) -> Promise {
  return Promise { resolve, reject in
    getAsyncPosts(url) { data, error in
      if let error = error {
        reject(error)
      }
      else {
        resolve(data)
      }
    }
  }
}

let promise = getPosts("http://swift-tuto.fr")

promise
  .then { data in
    print("posts", data)
  }
  .error { error in
    print("error", error)
  }

La fonction getPosts retourne une promesse de NSData. La promesse est construite la manière suivante. Elle définit 2 méthodes resolve et reject qui lui permettent de savoir quand la tâches est fini et ce de manière normal ou inattendu avec une erreur. Ici nous appelons getAsyncPosts et en fonction du résultat non appelons resolve ou reject avec les valeurs appropriés. De cette manière la promesse est capable de gérer les tâches synchrones elle même sans que nous ayons à nous en soucier.

Dans un deuxième temps nous appelons la méthode getPosts qui nous retourne une promesse que nous avons plus qu’a écouter. La méthode then est appelé uniquement si le resolve est appelé et error si c’est reject.

Cela peut vous paraitre encore un peu abstrait mais vous pouvez déjà vous rendre compte que l’on ne parle plus de file (queue), de thread ou autre. On fait abstraction de ça pour passer au niveau du dessus qui est l’enchaînement de tâche dont on ne sais même pas comment elles sont exécutés.

Examples

Un des plus grand avantage des promesses est leur capacité d’être chainé entre elles. Imaginons que vous deviez télécharger un zip, puis le dézipper, ensuite traiter ces données pour enfin les afficher à l’utilisateur. Voici comment nous pourrions écrire cela avec les promesses :

downloadZipAt("http://...")
  .then { file in
    return self.unzip(file)
  }
  .then { path in
    return processFilesAtPath(path)
  }
  .then { result in
    print(result)
  }
  .error { error in
    print("An error occurred:", error)
  }

Voilà ! Vous comprenez maintenant l’utilité dans promesses ? Ici si une erreur survient, et ce à n’importe quelle étape, l’enchainement des promesses est interrompu et le block de code dans error est appelé.

Ici on évite d’imbriquer les callbacks et le code devient beaucoup plus compréhensible car on comprend aisément l’enchainement des actions.

Bien sûr d’autres méthodes sont disponibles pour faire encore plein d’autre chose. Par example si vous avez un ensemble d’action à effectuer (peut importe l’ordre) avant d’effectuer d’autres tâches vous pouvez écrire ce genre de chose :

let tache1 = executeTache1Promise()
let tache2 = executeTache2Promise()
let tache3 = executeTache2Promise()

when(tache1, tache2, tache3)
  .then { reponse1, reponse2, reponse3 in
    // ...
  }
  .error { error in
  }

Pour ce genre de situation PromiseKit fournit la méthode when qui permet d’exécuter l’ensemble des tâches de manière asynchrone et d’attendre l’ensemble de leur complétion avant d’appelé le then. Si la moindre erreur survient dans l’une des tâche c’est error qui est appelé.

Conclusion

J’espère que vous avez apprécié ce petit tuto. Pour en savoir plus sur PromiseKit voici la documentation. Pour en savoir plus sur les promesses je vous conseille de jeter un coup d’oeil sur du coté de javascript. Comme d’habitude si vous avez des questions ou autre n’hésitez pas !

1 Etoile2 Etoiles3 Etoiles4 Etoiles5 Etoiles (2 votes, average: 5,00 out of 5)
Loading...

Aucun commentaire

Time limit is exhausted. Please reload CAPTCHA.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

  1. MichelP · 20 mai 2016

    Quand je parle de Threads, je parle de NSOperation