Promise en JavaScript: attendre le résultat d'un traitement

Pour effectuer un traitement qui dépend de l'achèvement d'un autre, on utilise l'objet Promise. Une démonstration simple.

Cet objet est surtout pratique lorsque l'on attend une série de résultats pour poursuivre les opérations. Il simplifie alors le code. Il vient en complément de la méthode setTimeout qui attend la fin d'un délai pour accomplir une action, et des listeners qui réagissent aux actions de l'utilisateur. Promise lui réagit à l'achèvement d'un processus.

On pourrait se demander, pourquoi ne pas placer le code qui dépend de cet achèvement, après celui-ci? Parce que le second fonctionne en mode asynchrone, et n'a aucune connexion avec le code qui appelle son exécution. Mais ce sera plus clair dans la démonstration ci-dessous.

Promise est implémenté sur tous les navigateurs récents, mais une proportion encore large de postes ne le reconnait pas. On l'utilisera donc dans une application moderne ou coté serveur. Il existe aussi des extensions proposées sur GitHub pour ajouter cette fonction aux anciens navigateurs.

Syntaxe

Le constructeur de l'objet promise à un argument, une fonction qui représente le traitement à accomplir avant de pouvoir continuer.

La méthode then d'une instance de l'objet est appelée avec deux argements. Ces arguments sont deux fonctions que l'on a préalablement définies. Quand le traitement attendu est terminé et est un succès, il appelle la première fonction. S'il échoue, il appelle la seconde fonction.

Maintenant on va voir cela dans un exemple:

1) Une fonction utilitaire pour afficher le résultat

function display(message) {
	document.getElementById("storage").innerHTML = message;
}

2) Le traitement à effectuer, dont on attend le résultat

La fonction a deux autres fonctions comme arguments. L'une ou l'autre est appelée selon que le processus est un succès ou un échec, et elle ont un message ou un résultat comme argument.

var A = 10;
var B = 5;

function p(success, fail) {
  if(A + B == 15) 
    success("C'est un succès");
  else
    fail("C'est un échec");
}

3) Définition du traitement différé qui répond au traitement en suspens

On définit deux fonctions, celle que l'on appelle quand le traitement est terminé et a réussi, et l'autre qui signale un échec.

function bon(res) {
  display(res);
}

function mauvais(res) {
  display(res);
}

Dans une application réelle, on fera bien sûr autre chose qu'afficher simplement un message comme dans cet exemple minimaliste.

4) Déclaration d'une instance de Promise et appel de then

On déclare une instance de Promise, avec la fonction en dont on attend le résultat comme paramètre du constructeur, puis on appelle la méthode then de cette instance avec en paramètre deux fonctions qui réagissent au succès ou à une erreur.

var promise = new Promise(p);
promise.then(bon, mauvais);

5) Résultat

Résultat

6) Contre-exemple

On va créer une nouvelle instance du même objet Promise, mais cette fois le résultat dépend d'une valeur choisie par vous.

Entrez un nombre dans le champ de texte ci-dessous et cliquez sur le bouton. Ce nombre sera assigné à la variable B. Si sa valeur est différente de 5, le script doit afficher "C'est un échec" dans la zone de résultat ci-dessus.

Voici le code utilisé:

<input id="userval" type="text" value=""><input type="button" onclick="setVal('userval')" value="Cliquez">

<script>
var A = 10;
var B = 5;

function display(message) {
  document.getElementById("storage").innerHTML = message;
}

function bon(res) {
  display(res);
}

function mauvais(res) {
  display(res);
}

function p(success, fail) {
  if(A + B == 15)
    success("C'est un succès");
  else
    fail("C'est un échec");
}
function setVal(id) {
  B = parseInt(document.getElementById("userval").value);
  var promise1 = new Promise(p);
  promise1.then(bon, mauvais);
}
</script>

Il est possible d'écrire le code précédent sous forme de callback. Ce serait alors moins lisible et on ne pourrait pas réutiliser les fonctions... dont la nature est d'être réutilisables. Contrairement à l'instance de Promise que l'on ne peut utiliser qu'une seule fois, selon mes essais, ce pourquoi il a fallu déclarer promise1.

Deuxième partie: Promise.all vs promise.race. Allez plus loin avec l'objet Promise, et gérez plusieurs traitements concurrents.

Voir aussi...

© 7 mai 2015 - Mis à jour en 2017 Xul.fr