L'objet Promise
(pour « promesse ») est utilisé pour réaliser des traitements de façon asynchrone. Une promesse représente une valeur qui peut être disponible maintenant, dans le futur voire jamais.
Syntaxe
new Promise( /* exécuteur */ function(resolve, reject) { ... } );
Paramètres
exécuteur
- Une fonction à laquelle on passera deux arguments :
resolve
etreject
. Cette fonction est exécutée immédiatement par l'implémentation dePromise
qui fournit les fonctionsresolve
etreject
(elle est exécutée avant que le constructeurPromise
ait renvoyé l'objet créé). Les fonctionsresolve
etreject
, lorsqu'elles sont appelées, permettent respectivement de tenir ou de rompre la promesse. On attend de l'exécuteur qu'il démarre un travail asynchrone puis, une fois le travail terminé, appelle la fonctionresolve
(si tout s'est bien passé) ou la fonctionreject
(lorsqu'il y a eu un problème) pour définir l'état final de la promesse.
Description
L'interface Promise
représente un intermédiaire (proxy) vers une valeur qui n'est pas nécessairement connue au moment de sa création. Cela permet d'associer des gestionnaires au succès éventuel d'une action asynchrone et à la raison d'une erreur. Ainsi, des méthodes asynchrones renvoient des valeurs comme les méthodes synchrones, la seule différence est que la valeur retournée par la méthode asynchrone est une promesse (d'avoir une valeur plus tard).
Une Promise
est dans un de ces états :
- pending (en attente) : état initial, la promesse n'est ni remplie, ni rompue ;
- fulfilled (tenue) : l'opération a réussi ;
- rejected (rompue) : l'opération a échoué ;
- settled (acquitée) : la promesse est tenue ou rompue mais elle n'est plus en attente.
Une promesse en attente peut être tenue avec une valeur ou rompue avec une raison (erreur). Quand on arrive à l'une des deux situations, les gestionnaires associés lors de l'appel de la méthode then
sont alors appelés. (Si la promesse a déjà été tenue ou rompue lorsque le gestionnaire est attaché à la promesse, le gestionnaire est appelé. Cela permet qu'il n'y ait pas de situation de compétition entre une opération asynchrone en cours et les gestionnaires ajoutés).
Les méthodes Promise.prototype.then()
et Promise.prototype.catch()
renvoient des promesses et peuvent ainsi être chaînées. C'est ce qu'on appelle une composition.
Note: Une promesse est dans l'état settled (acquittée) qu'elle soit tenue ou rompue mais plus en attente. Le terme resolved (résolue) est aussi utilisé concernant les promesses — cela signifie que la promesse est acquittée ou bien enfermée dans une chaine de promesse. Le billet de Domenic Denicola, States and fates (en anglais), contient de plus amples détails sur la terminologie utilisée.
Attention : D'autres langages utilisent des mécanismes d'évaluation à la volée (lazy evaluation) et de déport des calculs (deferring computations). Ces mécanismes sont également intitulés promesses (promises). En JavaScript, les promesses correspondent à des processus déjà lancés et qui peuvent être chaînés avec des fonctions de retour. Si vous cherchez à retarder l'évaluation, vous pouvez utiliser les fonctions fléchées sans arguments (ex. f = () => expression
) afin de créer une expression à évaluer plus tard et utiliser f()
pour l'évaluer au moment voulu.
Propriétés
Promise.length
- Une propriété de longueur qui vaut 1 (le nombre d'arguments pour le constructeur).
Promise.prototype
- Cette propriété représente le prototype du constructeur
Promise
.
Méthodes
Promise.all(itérable)
- Renvoie une promesse tenue lorsque toutes les promesses de l'argument itérable sont tenues ou une promesse rompue dès qu'une promesse de l'argument itérable est rompue. Si la promesse est tenue, elle est résolue avec un tableau contenant les valeurs de résolution des différentes promesses contenues dans l'itérable (dans le même ordre que celui-ci). Si la promesse est rompue, elle contient la raison de la rupture de la part de la promesse en cause, contenue dans l'itérable. Cette méthode est utile pour agréger les résultats de plusieurs promesses tous ensemble.
Promise.race(itérable)
- Renvoie une promesse qui est tenue ou rompue dès que l'une des promesses de l'itérable est tenue ou rompue avec la valeur ou la raison correspondante.
Promise.reject(raison)
- Renvoie un objet
Promise
qui est rompue avec la raison donnée.
Promise.resolve(valeur)
- Renvoie un objet
Promise
qui est tenue (résolue) avec la valeur donnée. Si la valeur possède une méthodethen
, la promesse renvoyée « suivra » cette méthode pour arriver dans son état, sinon la promesse renvoyée sera tenue avec la valeur fournie. Généralement, quand on veut savoir si une valeur est une promesse, on utiliseraPromise.resolve(valeur)
et on travaillera avec la valeur de retour en tant que promesse.
Promise
prototype
Propriétés
Promise.prototype.constructor
- Renvoie la fonction qui a créé le prototype d'une instance. Ce sera la fonction
Promise
par défaut.
Méthodes
Promise.prototype.catch(onRejected)
- Ajoute une fonction callback à utiliser en cas de rejet de la promesse. Elle renvoie une nouvelle promesse qui est résolue avec la valeur de retour du callback s'il est appelé ou avec la valeur de résolution initiale si la promesse est tenue (et non rejetée).
Promise.prototype.then(onFulfilled, onRejected)
- Ajoute des fonctions à utiliser en cas de résolution ou de rejet de la promesse et renvoie une nouvelle promesse qui est résolue avec la valeur de retour de la fonction utilisée en fonction de la résolution ou non.
Exemples
Créer un objet Promise
Dans le court exemple qui suit, on illustre le mécanisme d'une Promise
. La méthode testPromise()
est appelée chaque fois qu'on clique sur l'élément <button>
. Cette méthode crée une promesse qui sera tenue grâce à la fonction window.setTimeout()
, et avec la valeur comptePromesse (nombre commançant à 1) après 1s
à 3s
(aléatoire). Le constructeur Promise() est utilisé pour créer la promesse.
Le fait que la promesse soit tenue est simplement enregistré via un callback sur p1.then()
. Quelques indicateurs illustrent la manière dont la partie synchrone est découplée de la partie asynchrone.
'use strict'; var comptePromesse = 0; function testPromise() { var thisComptePromesse = ++comptePromesse; var log = document.getElementById('log'); log.insertAdjacentHTML('beforeend', thisComptePromesse + ') Started (<small>Début du code synchrone</small>)<br/>'); // on crée une nouvelle promesse : var p1 = new Promise( // La fonction de résolution est appelée avec la capacité de // tenir ou de rompre la promesse function(resolve, reject) { log.insertAdjacentHTML('beforeend', thisComptePromesse + ') Promise started (<small>Début du code asynchrone</small>)<br/>'); // Voici un exemple simple pour créer un code asynchrone window.setTimeout( function() { // On tient la promesse ! resolve(thisComptePromesse); }, Math.random() * 2000 + 1000); }); // On définit ce qui se passe quand la promesse est tenue // et ce qu'on appelle (uniquement) dans ce cas // La méthode catch() définit le traitement à effectuer // quand la promesse est rompue. p1.then( // On affiche un message avec la valeur function(val) { log.insertAdjacentHTML('beforeend', val + ') Promise fulfilled (<small>Fin du code asynchrone</small>)<br/>'); }).catch( // Promesse rejetée function() { console.log("promesse rompue"); }); log.insertAdjacentHTML('beforeend', thisComptePromesse + ') Promise made (<small>Fin du code synchrone</small>)<br/>'); }
L'exemple s'exécute lorsqu'on clique sur le bouton. Pour tester cet exemple, il est nécessaire d'utiliser un navigateur qui supporte les objets Promise
. En cliquant plusieurs fois sur le bouton en peu de temps, on verra qu'il y a plusieurs promesses tenues les une après les autres.
Utiliser XMLHttpRequest() avec une promesse
Dans l'exemple qui suit, on illustre comment utiliser une promesse pour fournir le résultat d'une requête XMLHttpRequest
:
'use strict'; // A-> $http cette fonction est implémentée pour respecter le patron // de conception (pattern) Adaptateur function $http(url){ // Un exemple d'objet var core = { // La méthode qui effectue la requête AJAX ajax : function (method, url, args) { // On établit une promesse en retour var promise = new Promise( function (resolve, reject) { // On instancie un XMLHttpRequest var client = new XMLHttpRequest(); var uri = url; if (args && (method === 'POST' || method === 'PUT')) { uri += '?'; var argcount = 0; for (var key in args) { if (args.hasOwnProperty(key)) { if (argcount++) { uri += '&'; } uri += encodeURIComponent(key) + '=' + encodeURIComponent(args[key]); } } } client.open(method, uri); client.send(); client.onload = function () { if (this.status >= 200 && this.status < 300) { // On utilise la fonction "resolve" lorsque this.status vaut 2xx resolve(this.response); } else { // On utilise la fonction "reject" lorsque this.status est différent de 2xx reject(this.statusText); } }; client.onerror = function () { reject(this.statusText); }; }); // Return the promise return promise; } }; // Pattern adaptateur return { 'get' : function(args) { return core.ajax('GET', url, args); }, 'post' : function(args) { return core.ajax('POST', url, args); }, 'put' : function(args) { return core.ajax('PUT', url, args); }, 'delete' : function(args) { return core.ajax('DELETE', url, args); } }; }; // Fin de A // B-> Ici on définit les fonctions et les charges utiles var mdnAPI = 'https://developer.mozilla.org/en-US/search.json'; var payload = { 'topic' : 'js', 'q' : 'Promise' }; var callback = { success : function(data){ console.log(1, 'success', JSON.parse(data)); }, error : function(data){ console.log(2, 'error', JSON.parse(data)); } }; // End B // On exécute la méthode $http(mdnAPI) .get(payload) .then(callback.success) .catch(callback.error); // Une alternative qui appelle la méthode et permet de gérer le rejet de la promesse différemment $http(mdnAPI) .get(payload) .then(callback.success, callback.error); // Une autre alternative pour la gestion du rejet de la promesse $http(mdnAPI) .get(payload) .then(callback.success) .then(undefined, callback.error);
Charger une image en XHR
Un autre exemple simple utilisant Promise
et XMLHttpRequest
afin de charger une image est disponible sur le dépôt GitHub MDN promise-test. Vous pouvez également consulter le résultat obtenu sur cette page. Chaque étape est commentée afin de vous permettre de suivre l'état de la promesse et l'architecture utilisée avec XHR.
Spécifications
Spécification | État | Commentaires |
---|---|---|
ECMAScript 2015 (6th Edition, ECMA-262) La définition de 'Promise' dans cette spécification. |
Standard | Définition initiale au sein d'un standard ECMA. |
ECMAScript 2017 Draft (ECMA-262) La définition de 'Promise' dans cette spécification. |
Projet |
Compatibilité des navigateurs
Pour contribuer à ces données de compatibilité, vous pouvez envoyer une poule requête sur : https://github.com/mdn/browser-compat-data/blob/master/javascript/promise.json.
Fonctionnalité | Chrome | Edge | Firefox | Internet Explorer | Opera | Safari | Servo |
---|---|---|---|---|---|---|---|
Promise | 32.0 | (Oui) | 29.01 | Aucun support | 19 | 7.12 | Aucun support |
Promise.all | 32.0 | (Oui) | 29.0 | Aucun support | 19 | 7.1 | Aucun support |
Promise.prototype | 32.0 | (Oui) | 29.0 | Aucun support | 19 | 7.1 | Aucun support |
Promise.prototype.catch | 32.0 | (Oui) | 29.0 | Aucun support | 19 | 7.1 | Aucun support |
Promise.prototype.then | 32.0 | (Oui) | 29.0 | Aucun support | 19 | 7.1 | Aucun support |
Promise.race | 32.0 | (Oui) | 29.0 | Aucun support | 19 | 7.1 | Aucun support |
Promise.reject | 32.0 | (Oui) | 29.0 | Aucun support | 19 | 7.1 | Aucun support |
Promise.resolve | 32.0 | (Oui) | 29.0 | Aucun support | 19 | 7.1 | Aucun support |
Fonctionnalité | Android | Chrome for Android | Edge Mobile | Firefox for Android | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|---|
Promise | 4.4.4 | 32.0 | (Oui) | 291 | Aucun support | (Oui) | 8.02 |
Promise.all | 4.4.4 | 32.0 | (Oui) | 29 | Aucun support | (Oui) | 8.0 |
Promise.prototype | 4.4.4 | 32.0 | (Oui) | 29 | Aucun support | (Oui) | 8.0 |
Promise.prototype.catch | 4.4.4 | 32.0 | (Oui) | 29 | Aucun support | (Oui) | 8.0 |
Promise.prototype.then | 4.4.4 | 32.0 | (Oui) | 29 | Aucun support | (Oui) | 8.0 |
Promise.race | 4.4.4 | 32.0 | (Oui) | 29 | Aucun support | (Oui) | 8.0 |
Promise.reject | 4.4.4 | 32.0 | (Oui) | 29 | Aucun support | (Oui) | 8.0 |
Promise.resolve | 4.4.4 | 32.0 | (Oui) | 29 | Aucun support | (Oui) | 8.0 |
1. Constructor requires a new operator since version 37.
2. Constructor requires a new operator since version 10.
Voir aussi
- Promises/A+ specification
- Jake Archibald : JavaScript Promises : There and Back Again (tutoriel en anglais)
- Domenic Denicola : Callbacks, Promises, and Coroutines – Asynchronous Programming Patterns in JavaScript (présentation en anglais sur l'asynchronisme en JavaScript)
- Matt Greer : JavaScript Promises ... In Wicked Detail (en anglais)
- Forbes Lindesay : promisejs.org (en anglais)
- Nolan Lawson : We have a problem with promises - Common mistakes with promises (en anglais)
- Prothèse pour les promesses par Jake Archibald
- Les promesses JavaScript sur Udacity (en anglais)