--- title: Gérer les opérations asynchrones avec élégance grâce aux promesses slug: Learn/JavaScript/Asynchronous/Promises tags: - Beginner - CodingScripting - Guide - JavaScript - Learn - Promises - async - asynchronous - catch - finally - then translation_of: Learn/JavaScript/Asynchronous/Promises --- {{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}} **Les promesses** sont une fonctionnalité relativement nouvelle du langage JavaScript qui vous permet de reporter d'autres actions jusqu'à ce qu'une action précédente soit terminée, ou de répondre à son échec. Ceci est utile pour mettre en place une séquence d'opérations asynchrones afin qu'elles fonctionnent correctement. Cet article vous montre comment les promesses fonctionnent, comment vous les verrez utilisées avec les API Web, et comment écrire les vôtres.
Prérequis : Connaissances informatiques de base, compréhension raisonnable des principes fondamentaux de JavaScript.
Objectif : Comprendre les promesses et savoir comment les utiliser.
## Que sont les promesses ? Nous avons examiné les [promesses (`Promise`)](/fr/docs/Web/JavaScript/Reference/Global_Objects/Promise) brièvement dans le premier article du cours, mais ici nous allons les examiner de manière beaucoup plus approfondie. Essentiellement, une promesse est un objet qui représente un état intermédiaire d'une opération - en fait, c'est une _promesse_ qu'un résultat d'une certaine nature sera retourné à un moment donné dans le futur. Il n'y a aucune garantie du moment exact où l'opération se terminera et où le résultat sera renvoyé, mais il _est_ une garantie que lorsque le résultat est disponible, ou que la promesse échoue, le code que vous fournissez sera exécuté afin de faire autre chose avec un résultat réussi, ou de gérer gracieusement un cas d'échec. En général, vous êtes moins intéressé par le temps qu'une opération asynchrone prendra pour renvoyer son résultat (à moins bien sûr qu'elle ne prenne _beaucoup_ trop de temps !), et plus intéressé par le fait de pouvoir répondre à son retour, quel que soit le moment. Et bien sûr, il est agréable que cela ne bloque pas le reste de l'exécution du code. L'une des utilisations les plus courantes des promesses concerne les API web qui renvoient une promesse. Considérons une hypothétique application de chat vidéo. L'application dispose d'une fenêtre contenant une liste des amis de l'utilisateur, et un clic sur un bouton à côté d'un utilisateur lance un appel vidéo vers cet utilisateur. Le gestionnaire de ce bouton appelle [`getUserMedia()`](/fr/docs/Web/API/MediaDevices/getUserMedia) afin d'avoir accès à la caméra et au microphone de l'utilisateur. Puisque `getUserMedia()` doit s'assurer que l'utilisateur a la permission d'utiliser ces dispositifs _et_ lui demander quel microphone utiliser et quelle caméra utiliser (ou s'il s'agit d'un appel vocal uniquement, parmi d'autres options possibles), il peut bloquer jusqu'à ce que non seulement toutes ces décisions soient prises, mais aussi que la caméra et le microphone soient activés. En outre, l'utilisateur peut ne pas répondre immédiatement à ces demandes d'autorisation. Cela peut potentiellement prendre beaucoup de temps. Puisque l'appel à `getUserMedia()` est effectué depuis le processus principal du navigateur, l'ensemble du navigateur est bloqué jusqu'à ce que `getUserMedia()` retourne une réponse ! Évidemment, ce n'est pas une option viable ; sans les promesses, tout dans le navigateur devient inutilisable jusqu'à ce que l'utilisateur décide ce qu'il faut faire de la caméra et du microphone. Ainsi, au lieu d'attendre l'utilisateur, d'obtenir l'activation des périphériques choisis et de retourner directement le [`MediaStream`](/fr/docs/Web/API/MediaStream) pour le flux créé à partir des sources sélectionnées, `getUserMedia()` retourne une [`promesse`](/fr/docs/Web/JavaScript/Reference/Global_Objects/Promise) qui est résolue avec le [`MediaStream`](/fr/docs/Web/API/MediaStream) une fois qu'il est disponible. Le code qu'utiliserait l'application de chat vidéo pourrait ressembler à ceci : ```js function handleCallButton(evt) { setStatusMessage("Appel..."); navigator.mediaDevices.getUserMedia({video: true, audio: true}) .then(chatStream => { selfViewElem.srcObject = chatStream; chatStream.getTracks().forEach(track => myPeerConnection.addTrack(track, chatStream)); setStatusMessage("Connecté"); }).catch(err => { setStatusMessage("Échec de la connexion"); }); } ``` Cette fonction commence par utiliser une fonction appelée `setStatusMessage()` pour mettre à jour un affichage d'état avec le message "Appel...", indiquant qu'un appel est tenté.Il appelle ensuite `getUserMedia()`, demandant un flux qui a à la fois des pistes vidéo et audio, puis une fois que cela a été obtenu, configure un élément vidéo pour montrer le flux provenant de la caméra comme une "vue de soi", puis prend chacune des pistes du flux et les ajoute à la [`RTCPeerConnection`](/fr/docs/Web/API/RTCPeerConnection) [WebRTC](/fr/docs/Web/API/WebRTC_API) représentant une connexion à un autre utilisateur. Après cela, l'affichage de l'état est mis à jour pour indiquer "Connecté". Si `getUserMedia()` échoue, le bloc `catch` s'exécute. Celui-ci utilise `setStatusMessage()` pour mettre à jour la case d'état afin d'indiquer qu'une erreur s'est produite. La chose importante ici est que l'appel `getUserMedia()` revient presque immédiatement, même si le flux de la caméra n'a pas encore été obtenu. Même si la fonction `handleCallButton()` est déjà retournée au code qui l'a appelée, lorsque `getUserMedia()` a fini de travailler, elle appelle le gestionnaire que vous fournissez. Tant que l'application ne suppose pas que le flux a commencé, elle peut continuer à fonctionner. > **Note :** Vous pouvez en apprendre davantage sur ce sujet quelque peu avancé, si cela vous intéresse, dans l'article [L'essentiel du WebRTC](/fr/docs/Web/API/WebRTC_API/Signaling_and_video_calling). Un code similaire à celui-ci, mais beaucoup plus complet, est utilisé dans cet exemple. ## Le problème des fonctions de rappel Pour bien comprendre pourquoi les promesses sont une bonne chose, il est utile de repenser aux anciennes fonctions de rappel (_callback_) et de comprendre pourquoi elles sont problématiques. Prenons l'exemple de la commande d'une pizza. Il y a certaines étapes que vous devez franchir pour que votre commande soit réussie, et cela n'a pas vraiment de sens d'essayer de les exécuter dans le désordre, ou dans l'ordre mais avant que chaque étape précédente ne soit tout à fait terminée : 1. Vous choisissez les garnitures que vous voulez. Cela peut prendre un certain temps si vous êtes indécis, et peut échouer si vous n'arrivez pas à vous décider, ou si vous décidez de prendre un curry à la place. 2. Vous passez ensuite votre commande. Le retour d'une pizza peut prendre un certain temps et peut échouer si le restaurant ne dispose pas des ingrédients nécessaires à sa cuisson. 3. Vous récupérez ensuite votre pizza et la mangez. Cela peut échouer si, par exemple, vous avez oublié votre portefeuille et ne pouvez pas payer la pizza ! Avec l'ancien modèle de [rappels](/fr/docs/Learn/JavaScript/Asynchronous/Introducing#callbacks), une représentation en pseudo-code de la fonctionnalité ci-dessus pourrait ressembler à quelque chose comme ceci : ```js chooseToppings(function(toppings) { placeOrder(toppings, function(order) { collectOrder(order, function(pizza) { eatPizza(pizza); }, failureCallback); }, failureCallback); }, failureCallback); ``` Cela est désordonné et difficile à lire (souvent appelé « [_callback hell_](http://callbackhell.com/) »), nécessite que le `failureCallback()` soit appelé plusieurs fois (une fois pour chaque fonction imbriquée), avec d'autres problèmes en plus. ### Améliorations avec des promesses Les promesses facilitent grandement l'écriture, l'analyse et l'exécution de situations telles que celle décrite ci-dessus. Si nous avions représenté le pseudo-code ci-dessus en utilisant des promesses asynchrones à la place, nous aurions obtenu quelque chose comme ceci : ```js chooseToppings() .then(function(toppings) { return placeOrder(toppings); }) .then(function(order) { return collectOrder(order); }) .then(function(pizza) { eatPizza(pizza); }) .catch(failureCallback); ``` C'est bien mieux - il est plus facile de voir ce qui se passe, nous n'avons besoin que d'un seul bloc `.catch()` pour gérer toutes les erreurs, cela ne bloque pas le processus principal (nous pouvons donc continuer à jouer à des jeux vidéo en attendant que la pizza soit prête à être collectée), et chaque opération a la garantie d'attendre que les opérations précédentes soient terminées avant de s'exécuter. Nous sommes en mesure d'enchaîner plusieurs actions asynchrones pour qu'elles se produisent les unes après les autres de cette façon, car chaque bloc `.then()` renvoie une nouvelle promesse qui se résout lorsque le bloc `.then()` a fini de s'exécuter. Astucieux, non ? En utilisant les fonctions flèches, vous pouvez simplifier encore plus le code : ```js chooseToppings() .then(toppings => placeOrder(toppings) ) .then(order => collectOrder(order) ) .then(pizza => eatPizza(pizza) ) .catch(failureCallback); ``` Ou encore ça : ```js chooseToppings() .then(toppings => placeOrder(toppings)) .then(order => collectOrder(order)) .then(pizza => eatPizza(pizza)) .catch(failureCallback); ``` Cela fonctionne car avec les fonctions flèches `() => x` est un raccourci valide pour `() => { return x ; }`. Vous pourriez même le faire ainsi, puisque les fonctions ne font que passer leurs arguments directement, et qu'il n'y a donc pas besoin de cette couche supplémentaire de fonctions : ```js chooseToppings().then(placeOrder).then(collectOrder).then(eatPizza).catch(failureCallback); ``` Cependant, la lecture n'est pas aussi facile et cette syntaxe peut ne pas être utilisable si vos blocs sont plus complexes que ce que nous avons montré ici. > **Note :** Vous pouvez apporter d'autres améliorations avec la syntaxe `async`/`await`, que nous aborderons dans le prochain article. Dans leur forme la plus basique, les promesses sont similaires aux écouteurs d'événements, mais avec quelques différences : - Une promesse ne peut réussir ou échouer qu'une seule fois. Elle ne peut pas réussir ou échouer deux fois et elle ne peut pas passer du succès à l'échec ou vice versa une fois l'opération terminée. - Si une promesse a réussi ou échoué et que vous ajoutez plus tard une de rappel de réussite/échec, la bonne fonction de rappel sera appelée, même si l'événement a eu lieu plus tôt. ## Explication de la syntaxe de base des promesses : exemple concret Il est important de comprendre les promesses, car la plupart des API Web modernes les utilisent pour les fonctions qui exécutent des tâches potentiellement longues. Pour utiliser les technologies Web modernes, vous devrez utiliser des promesses. Plus loin dans ce chapitre, nous verrons comment écrire votre propre promesse, mais pour l'instant, nous allons nous pencher sur quelques exemples simples que vous rencontrerez dans les API Web. Dans le premier exemple, nous allons utiliser la méthode [`fetch()`](/fr/docs/Web/API/WindowOrWorkerGlobalScope/fetch) pour récupérer une image sur le web, la méthode [`blob()`](/fr/docs/Web/API/Body/blob) pour transformer le contenu brut du corps de la réponse fetch en un objet [`Blob`](/fr/docs/Web/API/Blob), puis afficher ce blob à l'intérieur d'un élément [``](/fr/docs/Web/HTML/Element/Img). Cet exemple est très similaire à celui que nous avons examiné dans le [premier article](/fr/docs/Learn/JavaScript/Asynchronous/Introducing#asynchronous_javascript), mais nous le ferons un peu différemment au fur et à mesure que nous vous ferons construire votre propre code basé sur des promesses. > **Note :** L'exemple suivant ne fonctionnera pas si vous l'exécutez directement à partir du fichier (c'est-à-dire via une URL `file://`). Vous devez l'exécuter via un [serveur de test local](/fr/docs/Learn/Common_questions/set_up_a_local_testing_server), ou utiliser une solution en ligne telle que [Glitch](https://glitch.com/) ou [les pages GitHub](/fr/docs/Learn/Common_questions/Using_Github_pages). 1. Tout d'abord, téléchargez notre [modèle HTML simple](https://github.com/mdn/learning-area/blob/master/html/introduction-to-html/getting-started/index.html) et le [fichier image](https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/coffee.jpg) que nous allons récupérer. 2. Ajoutez un élément [`