Како написати ЈаваСцрипт обећање

Шта је обећање?

ЈаваСцрипт обећање је објекат који представља завршетак или неуспех асинхроног задатка и његову резултирајућу вредност.¹

Крај.

Шалим се наравно. Па, шта та дефиниција уопште значи?

Пре свега, многе ствари у ЈаваСцрипт-у су објекти. Објекат можете створити на неколико различитих начина. Најчешћи начин је са синтаксом литерарног објекта:

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

Можете и да креирате а classи направите инстанцу помоћу newкључне речи.

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

Обећање је једноставно објекат који креирамо као каснији пример. Инстанцирамо га помоћу newкључне речи. Уместо три параметра која смо унели да направимо свој аутомобил (боја, тип и врата), предајемо функцију која узима два аргумента: resolveи reject.

На крају, обећања нам говоре нешто о завршетку асинхроне функције из које смо је вратили - ако је радила или није. Кажемо да је функција била успешна рекавши да је обећање решено , а неуспешна рекавши да је обећање одбијено.

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

console.log(myPromise);

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

Видите, не превише застрашујуће - само објекат који смо створили. И, ако га мало проширимо:

Поред тога, можемо решити и одбити све што бисмо желели. На пример, могли бисмо да проследимо објекат уместо низа:

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

Или, као што смо раније видели, не морамо ништа да прођемо:

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

Шта је са „асинхроним“ делом дефиниције?

ЈаваСцрипт је једнонитни. То значи да истовремено може покретати само једну ствар. Ако можете да замислите пут, ЈаваСцрипт можете да замислите као аутопут са једном траком. Одређени код (асинхрони код) може се померити преко рамена како би омогућио да га други код пренесе. Када се тај асинхрони код заврши, враћа се на пут.

Као пратећу напомену можемо вратити обећање било које функције. Не мора бити асинхроно. То је речено, обећања се обично враћају у случајевима када је функција из које се враћају асинхрона. На пример, АПИ који има методе за чување података на серверу био би сјајан кандидат за враћање обећања!

За понети:

Обећања нам дају начин да сачекамо да се наш асинхрони код доврши, ухватимо неке вредности из њега и проследимо те вредности другим деловима нашег програма.

Овде имам чланак који дубље урања у ове концепте: Тхровн Фор Лооп: Разумевање петљи и временских ограничења у ЈаваСцрипт-у.

Како користимо обећање?

Коришћење обећања назива се и конзумирање обећања. У нашем примеру изнад, наша функција враћа објекат обећања. То нам омогућава да користимо уланчавање метода са нашом функцијом.

Ево примера уланчавања метода Кладим се да сте видели:

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

Сада се сетите нашег (претварајућег) обећања:

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

И, конзумирајући наше обећање помоћу ланца метода:

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

(Више) стварни пример.

Замислите да имате функцију која прима кориснике из базе података. Написао сам пример функције на Цодепену која симулира АПИ који бисте могли користити. Пружа две могућности за приступ резултатима. Прво, можете да обезбедите функцију повратног позива где можете приступити кориснику или било којој грешци. Или два, функција враћа обећање као начин приступа кориснику или грешку.

Традиционално бисмо резултатима асинхроног кода приступали коришћењем повратних позива.

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

Коришћење повратних позива је у реду док се не претјерано угнезде. Другим речима, са сваким новим резултатом морате покретати више асинхроног кода. Овај образац повратних позива унутар повратних позива може довести до нечега познатог као „пакао повратног позива“.

Обећања нам нуде елегантнији и читљивији начин да видимо ток нашег програма.

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. Обећања су објекти који садрже информације о довршењу неког асинхроног кода и све резултујуће вредности које желимо да проследимо.
  2. Да вратимо обећање које користимо return new Promise((resolve, reject)=> {})
  3. Да бисмо потрошили обећање које користимо .thenза добијање информација из обећања које је решено и .catchза добијање информација из обећања које је одбацило.
  4. Вероватно ћете више користити (трошити) обећања него што ћете писати.

Референце

1.) //девелопер.мозилла.орг/ен-УС/доцс/Веб/ЈаваСцрипт/Референце/Глобал_Објецтс/Промисе