Comment rédiger une promesse JavaScript

Qu'est-ce qu'une promesse?

Une promesse JavaScript est un objet qui représente l'achèvement ou l'échec d'une tâche asynchrone et sa valeur résultante.

La fin.

Je plaisante bien sûr. Alors, que signifie même cette définition?

Tout d'abord, de nombreux éléments en JavaScript sont des objets. Vous pouvez créer un objet de différentes manières. La méthode la plus courante consiste à utiliser la syntaxe littérale d'objet:

const myCar = { color: 'blue', type: 'sedan', doors: '4', };

Vous pouvez également créer un classet l'instancier avec le newmot - clé.

class Car { constructor(color, type, doors) { this.color = color; this.type = type; this.doors = doors } } const myCar = new Car('blue', 'sedan', '4');

console.log(myCar);

Une promesse est simplement un objet que nous créons comme le dernier exemple. Nous l'instancions avec le newmot - clé. Au lieu des trois paramètres que nous avons passés pour fabriquer notre voiture (couleur, type et portes), nous passons dans une fonction qui prend deux arguments: resolveet reject.

En fin de compte, les promesses nous disent quelque chose sur l'achèvement de la fonction asynchrone dont nous l'avons renvoyée - si cela a fonctionné ou non. Nous disons que la fonction a réussi en disant que la promesse est résolue , et qu'elle n'a pas réussi en disant que la promesse est rejetée.

const myPromise = new Promise(function(resolve, reject) {});

console.log(myPromise);

const myPromise = new Promise(function(resolve, reject) { resolve(10); });

Voyez, pas trop effrayant - juste un objet que nous avons créé. Et, si nous l'élargissons un peu:

De plus, nous pouvons transmettre tout ce que nous souhaitons résoudre et rejeter. Par exemple, nous pourrions passer un objet au lieu d'une chaîne:

return new Promise((resolve, reject) => { if(somethingSuccesfulHappened) { const successObject = { msg: 'Success', data,//...some data we got back } resolve(successObject); } else { const errorObject = { msg: 'An error occured', error, //...some error we got back } reject(errorObject); } });

Ou, comme nous l'avons vu précédemment, nous n'avons rien à passer:

return new Promise((resolve, reject) => { if(somethingSuccesfulHappend) { resolve() } else { reject(); } });

Qu'en est-il de la partie «asynchrone» de la définition?

JavaScript est à thread unique. Cela signifie qu'il ne peut exécuter qu'une seule chose à la fois. Si vous pouvez imaginer une route, vous pouvez considérer JavaScript comme une autoroute à une seule voie. Certains codes (code asynchrone) peuvent glisser sur l'épaule pour permettre à un autre code de le passer. Lorsque ce code asynchrone est terminé, il revient sur la chaussée.

En remarque, nous pouvons renvoyer une promesse à partir de n'importe quelle fonction. Il n'est pas nécessaire que ce soit asynchrone. Cela étant dit, les promesses sont normalement renvoyées dans les cas où la fonction qu'elles renvoient est asynchrone. Par exemple, une API qui a des méthodes pour enregistrer des données sur un serveur serait un excellent candidat pour retourner une promesse!

Les plats à emporter:

Les promesses nous donnent un moyen d'attendre que notre code asynchrone se termine, d'en capturer certaines valeurs et de les transmettre à d'autres parties de notre programme.

J'ai un article ici qui plonge plus profondément dans ces concepts: Thrown For a Loop: Understanding Loops and Timeouts in JavaScript.

Comment utilisons-nous une promesse?

Utiliser une promesse s'appelle également consommer une promesse. Dans notre exemple ci-dessus, notre fonction renvoie un objet de promesse. Cela nous permet d'utiliser le chaînage de méthodes avec notre fonction.

Voici un exemple de chaînage de méthodes que je parie que vous avez vu:

const a = 'Some awesome string'; const b = a.toUpperCase().replace('ST', '').toLowerCase(); console.log(b); // some awesome ring

Maintenant, rappelez-vous notre (prétendue) promesse:

const somethingWasSuccesful = true; function someAsynFunction() { return new Promise((resolve, reject){ if (somethingWasSuccesful) { resolve(); } else { reject() } }); }

Et, en tenant notre promesse en utilisant le chaînage de méthodes:

someAsyncFunction .then(runAFunctionIfItResolved(withTheResolvedValue)) .catch(orARunAfunctionIfItRejected(withTheRejectedValue));

Un (plus) vrai exemple.

Imaginez que vous ayez une fonction qui récupère les utilisateurs d'une base de données. J'ai écrit un exemple de fonction sur Codepen qui simule une API que vous pourriez utiliser. Il propose deux options pour accéder aux résultats. Premièrement, vous pouvez fournir une fonction de rappel où vous pouvez accéder à l'utilisateur ou à toute erreur. Ou deux, la fonction renvoie une promesse comme moyen d'accéder à l'utilisateur ou à l'erreur.

Traditionnellement, nous accédions aux résultats du code asynchrone grâce à l'utilisation de rappels.

rr someDatabaseThing(maybeAnID, function(err, result)) { //...Once we get back the thing from the database... if(err) { doSomethingWithTheError(error) } else { doSomethingWithResults(results); } }

L'utilisation des rappels est correcte jusqu'à ce qu'ils deviennent trop imbriqués. En d'autres termes, vous devez exécuter plus de code asynchrone à chaque nouveau résultat. Ce modèle de rappels dans les rappels peut conduire à quelque chose appelé «l'enfer des rappels».

Les promesses nous offrent une manière plus élégante et lisible de voir le déroulement de notre programme.

doSomething() .then(doSomethingElse) // and if you wouldn't mind .catch(anyErrorsPlease);

Writing our own promise: Goldilocks, the Three Bears, and a Supercomputer

Imagine you found a bowl of soup. You’d like to know the temperature of that soup before you eat it. You're out of thermometers, but luckily, you have access to a supercomputer that tells you the temperature of the bowl of soup. Unfortunately, this supercomputer can take up to 10 seconds to get the results.

Here are a couple of things to notice.

  1. We initiate a global variable called result.
  2. We simulate the duration of the network delay with Math.random() and setTimeout().
  3. We simulate a temperature with Math.random().
  4. We keep the delay and temperature values confined within a range by adding some extra “math”. The range for temp is 1 to 300; the range for delay is 1000ms to 10000ms (1s to 10 seconds).
  5. We log the delay and temperature so we have an idea of how long this function will take and the results we expect to see when it’s done.

Run the function and log the results.

getTemperature(); console.log(results); // undefined

The temperature is undefined. What happened?

The function will take a certain amount of time to run. The variable is not set until the delay is over. So while we run the function, setTimeout is asynchronous. The part of the code in setTimeout moves out of the main thread into a waiting area.

I have an article here that dives deeper into this process: Thrown For a Loop: Understanding Loops and Timeouts in JavaScript.

Since the part of our function that sets the variable result moves into a holding area until it is done, our parser is free to move onto the next line. In our case, it’s our console.log(). At this point, result is still undefined since our setTimeout is not over.

So what else could we try? We could run getTemperature() and then wait 11 seconds (since our max delay is ten seconds) and then console.log the results.

getTemperature(); setTimeout(() => { console.log(result); }, 11000); // Too Hot | Delay: 3323 | Temperature: 209 deg

This works, but the problem with this technique is, although in our example we know the maximum network delay, in a real-life example it might occasionally take longer than ten seconds. And, even if we could guarantee a maximum delay of ten seconds, if the result is ready sooner, we are wasting time.

Promises to the Rescue

We are going to refactor our getTemperature() function to return a promise. And instead of setting the result, we will reject the promise unless the result is “Just Right,” in which case we will resolve the promise. In either case, we will pass in some values to both resolve and reject.

We can now use the results of our promise we are returning (also know as consuming the promise).

getTemperature() .then(result => console.log(result)) .catch(error => console.log(error)); // Reject: Too Cold | Delay: 7880 | Temperature: 43 deg

.then will get called when our promise resolves and will return whatever information we pass into resolve.

.catch will get called when our promise rejects and will return whatever information we pass into reject.

Most likely, you’ll consume promises more than you will create them. In either case, they help make our code more elegant, readable, and efficient.

Summary

  1. Promises are objects that contain information about the completion of some asynchronous code and any resulting values we want to pass in.
  2. To return a promise we use return new Promise((resolve, reject)=> {})
  3. To consume a promise we use .then to get the information from a promise that has resolved, and .catch to get the information from a promise that has rejected.
  4. You’ll probably use (consume) promises more than you’ll write.

References

1.) //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise