📜 ⬆️ ⬇️

ECMAScript 6 Promises

On Habré already met articles about the wonderful technology Promises, which in the future will become part of the ECMAScript 6 standard, however, these articles did not contain a detailed description of why they are so useful and what are their advantages. In order to fill this gap, I decided to write this article.

Attention! This article is not an exhaustive guide. This is rather a PR of good and useful technology, luring, showing positive sides. The article is a compilation of several other articles, links below.

So what is a Promise?



')

Important basics




Promise States


  1. Fulfilled - the calculation was successful
  2. Rejected - error in the calculation (any)
  3. Pending - the calculation has not yet completed (not fulfilled and not rejected)
  4. Settled - the calculation is complete (no matter how)


Examples


So, take a piece of synchronous code
function foo() { var a = 'a'; a = a + 'b'; a = a + 'c'; return a; } 

And let's make it look like a Promise from the outside:
 function foo() { var a = 'a'; a = a + 'b'; a = a + 'c'; return Promise.resolve(a); } 

The next step is to divide each step of the calculation:
 function foo() { return Promise.resolve('a') .then(function(a){ return a + 'b'; }) .then(function(a){ return a + 'c'; }); } 

Now each step can be made asynchronous, with the entire execution still being consistent.
Let's go ahead, replace one of the steps with “as if an asynchronous function returning Promise”:
 function getB(a){ return Promise.resolve(a + 'b'); } function foo() { return Promise.resolve('a') .then(function(a){ return getB(a); }) .then(function(a){ return a + 'c'; }); } 

The built-in functionality allows returning to then (cb ()) either an error (throw new Error ()) or a value (return a + 'c';) or the next Promise.

Parallelism


Imagine that you first need to perform asynchronous action 1, then in parallel 2 and 3, and then 4.
 asyncAction1() .then(function(res1){ return Promise.all([async2(res1), async3(res1)]); }) .then(function(arr){ // an array of values var res2 = arr[0], res3 = arr[1]; return asyncAction4(res2, res3); }) .then(…); 


Error processing


The most wonderful thing about Promises is error handling. It does not matter at what stage and in what depth of nesting an error occurred, whether it is a reject or just an exception thrown, all this can be caught and processed, or it can be passed further.
 asyncAction() .catch(function(rejection){ //  ,   -   if (rejection.code == 'foo') return 'foo'; //  ,    throw rejection; }) .then(…) .then(…) .catch(…); 

Here we need to make a remark that if we set
 var p1 = new Promise(...), p2 = new Promise(...) p3 = Promise.all([p1, p2]); p3.then(...).catch(...); 

Catch will catch everything that went wrong (and it doesn't matter what or how) in p1, p2, p3 and any nested calls, which is wildly convenient. Reverse - if catch () is not present, the error will be silently swallowed. However, Q-type libraries, as a rule, have the ability to specify an error handler for uncaught errors, where they can be displayed in the console or something else can be done.

A word about anti-patterns


 function anAsyncCall() { var promise = doSomethingAsync(); promise.then(function(){ somethingComplicated(); }); return promise; } 

With a flick of the wrist, we lost the second Promise. The fact is that each .then () or .catch () call creates a new Promise, so if you create a new one and return the old one, the new one will hang somewhere in the air and no one will know what the result of the calculation is. How to fight - just return the new Promise:
 return promise.then(...); 


Useful nishtyaki



Execution delay


 function delay(ms){ return new Promise(function(resolve){ setTimeout(resolve, ms); } }; 

Usage example
 delay(5000).then(…); 


Simplest timeout


Since the Promise can only be settled once (the rest is ignored), you can write something like
 function timeout(promise, ms) { return new Promise(function (resolve, reject) { promise.then(resolve); setTimeout(function () { reject(new Error('Timeout')); }, ms); }); } timeout(asyncAction(), 5000).then(…).catch(…); 

Who first got up - that and sneakers.

Slightly improved timeout


A slightly more obvious example of a timeout is through the "static" function Promise.race ().
 Promise.race([ asynchronousAction(), delay(5000).then(function () { throw new Error('Timed out'); }) ]) .then(function (text) { ... }) .catch(function (reason) { ... }); 


Important note about asynchrony


  1. The library controls the execution process, respectively, it manages how the result is delivered - synchronously or asynchronously.
  2. At the same time, the Promises / A + specification requires that the last mode is always used - asynchronous
  3. Thus, we can always rely on the immediate execution of promise.then (). Catch () code, etc., and not care that some of their callbacks will eat up all the CPU time (with reservations, of course)


Why Promises Better Callbacks




disadvantages


  1. Not suitable for recurring events (true, Promises were not written in order)
  2. Not suitable for streams (likewise)
  3. The current implementation in browsers does not allow monitoring progress (they are also going to be included in the standard)


JQuery note


The implementation of “Thennable” in jQuery is slightly different from the standard one - there is exactly one argument in the callback, in jQuery there are more arguments in jQuery, which does not prevent us from using the object obtained from jQuery in the native implementation. As a rule, more than the first argument, nothing from jQuery is needed. Plus there is a design:
 var jsPromise = Promise.resolve($.ajax('/whatever.json')); 


Read links


Source: https://habr.com/ru/post/242767/


All Articles