Sådan skriver du et JavaScript-løfte

Hvad er et løfte?

Et JavaScript-løfte er et objekt, der repræsenterer afslutningen eller fiaskoen af ​​en asynkron opgave og dens resulterende værdi

Afslutningen.

Jeg tuller selvfølgelig. Så hvad betyder definitionen endda?

Først og fremmest er mange ting i JavaScript objekter. Du kan oprette et objekt et par forskellige måder. Den mest almindelige måde er med bogstavelig syntaks:

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

Du kan også oprette en classog instantiere den med newnøgleordet.

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);

Et løfte er simpelthen et objekt, som vi skaber som det senere eksempel. Vi instantierer det med newnøgleordet. I stedet for de tre parametre, vi sendte ind for at fremstille vores bil (farve, type og døre), passerer vi en funktion, der tager to argumenter: resolveog reject.

I sidste ende fortæller løfter os noget om afslutningen af ​​den asynkrone funktion, vi returnerede den fra - hvis den fungerede eller ikke. Vi siger, at funktionen var vellykket ved at sige løftet løst og mislykket ved at sige løftet afvist.

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

console.log(myPromise);

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

Se, ikke for skræmmende - bare et objekt, vi skabte. Og hvis vi udvider det lidt:

Derudover kan vi videregive alt, hvad vi gerne vil løse, og afvise. For eksempel kunne vi videregive et objekt i stedet for en streng:

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); } });

Eller som vi så tidligere behøver vi ikke videregive noget:

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

Hvad med den “asynkrone” del af definitionen?

JavaScript er enkelt gevind. Dette betyder, at det kun kan køre en ting ad gangen. Hvis du kan forestille dig en vej, kan du tænke på JavaScript som en motorvej med en enkelt bane. Visse koder (asynkron kode) kan glide over på skulderen for at lade anden kode passere den. Når den asynkrone kode er færdig, vender den tilbage til kørebanen.

Som en sidebemærkning kan vi returnere et løfte fra enhver funktion. Det behøver ikke at være asynkront. Når det er sagt, returneres løfter normalt i tilfælde, hvor den funktion, de vender tilbage fra, er asynkron. For eksempel ville en API, der har metoder til at gemme data på en server, være en god kandidat til at returnere et løfte!

Takeaway:

Løfter giver os en måde at vente på, at vores asynkrone kode er færdig, fange nogle værdier fra den og videregive disse værdier til andre dele af vores program.

Jeg har en artikel her, der dykker dybere ned i disse begreber: Thrown For a Loop: Understanding Loops and Timeouts in JavaScript.

Hvordan bruger vi et løfte?

Brug af et løfte kaldes også at forbruge et løfte. I vores eksempel ovenfor returnerer vores funktion et løfteobjekt. Dette giver os mulighed for at bruge metodekædning med vores funktion.

Her er et eksempel på metodekædning, jeg ved, du har set:

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

Husk nu vores (foregiv) løfte:

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

Og forbruge vores løfte ved hjælp af metodekæde:

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

Et (mere) rigtigt eksempel.

Forestil dig, at du har en funktion, der får brugere fra en database. Jeg har skrevet et eksempel på Codepen, der simulerer en API, du måske bruger. Det giver to muligheder for at få adgang til resultaterne. Den ene, du kan give en tilbagekaldsfunktion, hvor du kan få adgang til brugeren eller enhver fejl. Eller to, funktionen returnerer et løfte som en måde at få adgang til brugeren eller fejlen.

Traditionelt ville vi få adgang til resultaterne af asynkron kode ved hjælp af tilbagekald.

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

Brug af tilbagekald er ok, indtil de bliver alt for indlejrede. Med andre ord skal du køre mere asynkron kode med hvert nye resultat. Dette mønster af tilbagekald inden for tilbagekald kan føre til noget kendt som "tilbagekald helvede."

Løfter giver os en mere elegant og læsbar måde at se strømmen af ​​vores program på.

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. Løfter er objekter, der indeholder information om færdiggørelsen af ​​en asynkron kode og eventuelle resulterende værdier, som vi ønsker at videregive.
  2. For at returnere et løfte, vi bruger return new Promise((resolve, reject)=> {})
  3. For at forbruge et løfte bruger vi .thentil at hente oplysningerne fra et løfte, der er løst, og .catchtil at få oplysningerne fra et løfte, der er afvist.
  4. Du bruger sandsynligvis (forbruger) løfter mere, end du skriver.

Referencer

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