--- tags: ironhack, lecture, --- <style> .markdown-body img[src$=".png"] {background-color:transparent;} .alert-info.lecture, .alert-success.lecture, .alert-warning.lecture, .alert-danger.lecture { box-shadow:0 0 0 .5em rgba(64, 96, 85, 0.4); margin-top:20px;margin-bottom:20px; position:relative; ddisplay:none; } .alert-info.lecture:before, .alert-success.lecture:before, .alert-warning.lecture:before, .alert-danger.lecture:before { content:"👨‍🏫\A"; white-space:pre-line; display:block;margin-bottom:.5em; /*position:absolute; right:0; top:0; margin:3px;margin-right:7px;*/ } b { --color:yellow; font-weight:500; background:var(--color); box-shadow:0 0 0 .35em var(--color),0 0 0 .35em; } .skip { opacity:.4; } </style> ![Ironhack logo](https://i.imgur.com/1QgrNNw.png) # ES6 Promises ## Learning Goals After this lesson you will be able to: - Understand what **ES6 Promises** are - Learn how to implement **ES6 Promises** - Understand the different use cases where we can implement **Promises** ## Promises :::info lecture Une promesse est une action qui se déroulera dans le temps. Une promesse pourra être réalisée ou non. Par ex, tu me prêtes de l'argent : je te fais la promesse de te rembourser. ::: A `Promise` is an object representing the eventual **completion or failure** of an **asynchronous operation**. Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function. `Promises` give us a way to handle **asynchronous processing** in a more **synchronous fashion**. They represent a value that we can handle at some point in the future. And, better than callbacks here, Promises give us guarantees about that future value, specifically: - No other registered handlers of that value can change it (the `Promise` is immutable) - We are guaranteed to receive the value, regardless of when we register a handler for it, even if it's already resolved (in contrast to events, which can incur race conditions). ## Creating Promises The standard way to create a `Promise` is by using the `new Promise` **constructor** which accepts a handler that is given two functions as parameters. The first handler (typically named **`resolve`**) is a function to call with the future value when it's ready; and the second handler (typically named **`reject`**) is a function to call to reject the `Promise` if it can't resolve the future value. :::info lecture En voici la syntaxe : - L1 : constructor `new Promise(…)` - L1 : fonction en paramètre, avec 2 arguments `resolve` et `reject` - L4, la promesse est tenue - L6, la promesse n'est pas tenue ::: ```javascript= const loan = new Promise(function(resolve, reject) { // an action that takes time... setTimeout(function () { if (/* honest */) { resolve(/* value */); // fulfilled successfully } else { reject(/* reason */) // rejected } }, 10000) }); ``` :::info lecture Avant qu'une promesse ne soit tenue ou rompue, elle sera dans l'état `Pending`. ::: In this way, a `Promise` itself has one of the following three states: - **Pending**. Until a Promise is fulfilled it is in pending state - **Fulfilled**. When the first handler (typically `resolve`) is called the `Promise` is considered fulfilled with the value passed to that handler. - **Rejected**. If the second handler (typically `reject`) is called, the Promise is considered rejected with the value passed to that handler. :::info lecture Une promesse ne pourra etre tenue ou rompue qu'une seule fois. ::: **A `Promise` can only be "settled" (meaning it has been fulfilled or rejected) once.** You can also create an immediately resolved `Promise` by using the **`Promise.resolve()`** method. :::info lecture On peut d'ailleurs créer une promesse déjà tenue : ::: ```javascript= const ironhack = Promise.resolve(42); ``` :::info lecture On verra plus tard que cela pourra servir à chaîner avec une autre promesse (cf. `.then`)... ::: ## Consuming Promises :::info lecture Une fois réalisée (tenue ou rompue), nous allons vouloir réalisée des actions en fonction. ::: Once we have a `Promise`, it can be passed around as a **value**. The `Promise` is a stand-in for a future value, and so it can be returned from a `function`, passed as a `parameter` and used in any other way a standard value would be used. **To consume the `Promise`** - *meaning we want to process the `Promises` value once fulfilled* - we attach a `handler` to the `Promise` using it's `.then()` method. This method takes a `function` that will be passed the `resolved` value of the `Promise` once it is **fulfilled**. :::info lecture `.then` va nous permettre de réaliser une action si la promesse a été tenue : ```javascript loan.then(function (value) { console.log('ouiiii, le pret a été remboursé', value); }) ``` NB: `value` vaut alors la valeur passée lors du `resolve(value)` précédent ::: ```javascript= const p = new Promise((resolve, reject) => resolve("Ironhack")); p.then((val) => console.log(val)); // Ironhack ``` :::info lecture En vrai, `.then` accepte également un 2e paramètre: la fonction en cas d'échec, mais nous préfèrerons utiliser `.catch` pour cela... skip ::: A **Promise's `.then()`** method actually takes **two possible parameters**. - The first is the function to be called when the `Promise` is **fulfilled**. - The second is a function to be called if the `Promise` is **rejected**. ```javascript= p.then((val) => console.log("fulfilled:", val), (err) => console.log("rejected: ", err)); ``` :eyes: You can omit either handler in a `.then()`, so sending a `null` as the first handler and providing the second is the same as the standard `Promise.catch()`, which takes a single handler to be called when a **promise is rejected**. The following are equivalent: ```javascript= p.then((val) => console.log("fulfilled:", val)) .catch((err) => console.log("rejected:", err)); p.then((val) => console.log("fulfilled:", val)) .then(null, (err) => console.log("rejected:", err)); ``` And, as shown above `.then()` calls can be chained. If a handler returns a value in the a `.then()` call it is automatically wrapped in a `Promise` when returned and then properly unwrapped to pass the value to further chained `.then()` calls. ## Dealing with errors :::info lecture `.catch` va lui nous permettre de réaliser une action si la promesse a été rompue : ```javascript loan.then(function (value) { console.log('ouiiii, le pret a été remboursé', value); }).catch(function (reason) { console.log('hmmm!', reason) }); ``` NB: `reason` vaut alors la valeur passée lors du `reject(value)` précédent. ::: You should use **`.catch()`** for *handling errors*, rather than `.then(null, function)`. Using **`.catch()`** is more explicit and idiomatic, and when chaining you can have a single `.catch()` at the end of the chain to handle **any rejection or thrown exceptions** from either the original promise or any of it's handlers. :::info lecture Pour rejeter une promesse, il est également possible de `throw`er une erreur (au lieu d'utiliser `reject`) : ```javascript const loan = new Promise(function(resolve, reject) { // an action that takes time... setTimeout(function () { if (/* honest */) { resolve(/* value */); // fulfilled successfully } else { throw new Error('🤥') // rejected } }, 10000); }); ``` ::: Throwing an exception in a `Promise` will automatically **reject** that `Promise` as well. This is the same for `.then()` handlers and their **results** and **return** values as well. A thrown error is wrapped in a `Promise` and treated as a rejection. ### Example ```javascript= const p1 = new Promise((resolve, reject) => { if (true) throw new Error("rejected!"); else resolve(4); }); p1.then((val) => val + 2) .then((val) => console.log("got", val)) .catch((err) => console.log("error: ", err.message)); // => error: rejected! ``` ![](https://s3-eu-west-1.amazonaws.com/ih-materials/uploads/upload_9df97f56698a5721fa8b63445515c6e8.png) ## Promises All There will be sometime we will need to wait more than one `Promise` to complete to continue our program, no worries, you can use `Promise.all()`:wink: :::info lecture Il peut être utile d'attendre plusieurs promesses en même temps. <b>`Promise.all()` retourne une promesse de toutes les promesses</b> passées en tableau : ```javascript= function hasard(resolve, reject) { setTimeout(function () { if (Math.random() > .5) { resolve('lucky boy'); } else { reject('not your day') } }, 2000); } const p1 = new Promise(hasard); const p2 = new Promise(hasard); const p3 = new Promise(hasard); Promise.all([p1, p2, p3]).then(function (values) { // p1, p2 et p3 sont maintenant résolues }).catch(function (reason) { // l'une d'elle vient d'échouer }) ``` NB : - L13 : `values` est un tableau des différentes `resolve(value)` de chaque promesse - L15 : `reason` vaut la valeur passée par `reject()` L5 ::: The **`Promise.all()`** method returns a single `Promise` that **resolves when all of the promises in the iterable argument have resolved** or when the iterable argument contains no promises. It **rejects with the reason of the first promise that rejects.** This method can be useful for aggregating the results of multiple promises. **Fulfillment** - If an empty iterable is passed, then this method returns (synchronously) an already resolved promise. - If all of the passed-in promises fulfill, or are not promises, the promise returned by `Promise.all` is fulfilled asynchronously. - In all cases, the **returned promise is fulfilled with an array containing all the values of the iterable passed as argument (also non-promise values).** **Rejection** If any of the passed-in promises **reject**, `Promise.all` asynchronously rejects with the value of the **promise that rejected**, whether or not the other promises have resolved. ```javascript= const p1 = Promise.resolve(3); const p2 = 1337; const p3 = new Promise((resolve, reject) => { setTimeout(resolve, 100, 'foo'); }); Promise.all([p1, p2, p3]).then(values => { console.log(values); // [3, 1337, "foo"] }); ``` ### Promises Visualization ```javascript= Promise.all([ new Promise(resolve => setTimeout(resolve, 1500)), new Promise(resolve => setTimeout(resolve, 900)), new Promise(resolve => setTimeout(resolve, 2200)) ]) .then(results => results.length.b.c) .then(c => console.info(c)) .catch(err => console.error(err)) ``` ![](https://s3-eu-west-1.amazonaws.com/ih-materials/uploads/upload_05ef9031bc613f497b0a32a899af2f7c.png) Let's see in a more graphically way how the promises work, check out this [Promises Visualization](https://bevacqua.github.io/promisees/#) web, where we can see how each of the promises are been resolved after each of the `setTimeout`. Go ahead and play around with the different methods of `Promise`! :muscle: ## Summary We just learn a super powerfull tool for dealing with asynchrnous code. `Promises` give us the ability to write asynchronous code in a synchronous fashion, with flat indentation and a single exception channel. `Promises` give us guarantees of no race conditions and immutability of the future value represented by the `Promise` (unlike callbacks and events). ## Extra Resources - [Promises for Dummies](https://scotch.io/tutorials/javascript-promises-for-dummies)