Hogyan írhatunk JavaScript ígéretet

Mi az ígéret?

A JavaScript ígéret egy olyan objektum, amely egy aszinkron feladat befejezését vagy meghiúsulását és az abból eredő értéket képviseli.¹

Vége.

Vicceltem természetesen. Szóval, mit is jelent ez a meghatározás?

Először is sok dolog a JavaScript-ben objektum. Objektumot többféle módon hozhat létre. A leggyakoribb módszer az objektum szó szerinti szintaxissal:

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

Létrehozhat egy classés példányosíthatja is a newkulcsszóval.

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

Az ígéret egyszerűen egy objektum, amelyet úgy hozunk létre, mint a későbbi példa. A newkulcsszóval példányosítjuk . Az autónk gyártásához átadott három paraméter (szín, típus és ajtók) helyett egy olyan függvényt adunk át, amely két érvet tartalmaz: resolveés reject.

Végül az ígéretek mondanak valamit az aszinkron funkció befejezéséről, ahonnan visszaadtuk - ha működött vagy nem. Azt mondjuk, hogy a funkció sikeres volt az ígéret megoldásának mondásával , és sikertelen az ígéret elutasításával.

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

console.log(myPromise);

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

Lásd, nem túl ijesztő - csak egy objektum, amelyet létrehoztunk. És ha kicsit kibővítjük:

Ezen felül bármit átadhatunk, amit el akarunk oldani és elutasítunk. Például átadhatunk egy objektumot karakterlánc helyett:

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

Vagy, mint korábban láttuk, nem kell semmit sem átadnunk:

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

Mi a helyzet a definíció „aszinkron” részével?

A JavaScript egyszálú. Ez azt jelenti, hogy egyszerre csak egy dolgot tud futtatni. Ha el tud képzelni egy utat, akkor a JavaScriptet egyetlen sávos autópályának gondolhatja. Bizonyos kód (aszinkron kód) átcsúszhat a vállra, hogy más kód átengedje azt. Amikor az aszinkron kód elkészült, visszatér az úttestre.

Mellékképként megadhatunk ígéretet bármely funkcióból. Ennek nem kell aszinkronnak lennie. Ennek ellenére az ígéreteket általában olyan esetekben adják vissza, amikor a funkció, amelyből visszatérnek, aszinkron. Például egy API, amelynek módszerei vannak az adatok szerverre mentésére, remek jelölt lenne egy ígéret visszaküldésére!

Az elvitel:

Az ígéretek módot kínálnak arra, hogy megvárjuk az aszinkron kódunk elkészülését, elfogjunk belőle néhány értéket, és továbbadjuk ezeket az értékeket programunk más részeinek.

Van egy cikkem, amely mélyebben belemerül ezekbe a fogalmakba: Hurokért dobva: A hurkok és az időtúllépés megértése a JavaScript-ben.

Hogyan tudjuk felhasználni az ígéretet?

Az ígéret felhasználását ígéret elfogyasztásának is nevezzük . A fenti példánkban a függvényünk egy ígéret objektumot ad vissza. Ez lehetővé teszi számunkra, hogy a módszerünkkel láncoljunk a funkciónkkal.

Íme egy példa a módszerláncolásra, amelyre tippeltem:

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

Emlékezzünk vissza (tettetni) ígéretünket:

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

És ígéretünket a módszerláncolás felhasználásával elfogyasztjuk:

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

Egy (több) valós példa.

Képzelje el, hogy van egy olyan funkciója, amely a felhasználókat egy adatbázisból szerzi be. Írtam egy példa függvényt a Codepen-en, amely egy esetlegesen használt API-t szimulál. Két lehetőséget kínál az eredmények eléréséhez. Az egyik egy visszahívási funkciót biztosít, ahol elérheti a felhasználót vagy bármilyen hibát. Vagy két, a függvény ígéretet ad vissza a felhasználó vagy hiba elérésének módjaként.

Hagyományosan az aszinkron kód eredményeihez visszahívások segítségével jutunk hozzá.

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

A visszahívások használata rendben van, amíg túlzottan be nem ágyazódnak. Más szavakkal, minden új eredményhez több aszinkron kódot kell futtatnia. Ez a visszahívásokon belüli visszahívási mintázat vezethet úgynevezett „visszahívási pokolhoz”.

Az ígéretek elegánsabb és olvashatóbb módot kínálnak számunkra, hogy lássuk programunk menetét.

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