📜 ⬆️ ⬇️

Fallback actions in ES6 Promise

Good day to all!

It all started with the fact that as a test task at the interviews, I began to ask applicants to implement the preloader of pictures on JS. In addition to the preloading itself, the script should have been able to substitute the fallback picture if the desired picture could not be loaded. A prerequisite was the use of ES6 Promise.
Then I thought: “Why not implement such a preloader yourself and put it in common use? But this is also an excellent reason to write an article on Habr! ”.
Actually, under the cut description of the logic of the preloader + link to the preloader itself.

First, let's remember what is promis in JS.

About promises
Promis is, first of all, a way to organize asynchronous code. Although not necessarily asynchronous ...
To create a promise, you need a function that will be executed immediately after the promise is created.
Our task, inside this function, is to call one of two methods that are passed to this function automatically - resolve or reject.
By calling one of these methods, we tell the promise about the status of the task: successful (resolve) or unsuccessful (reject).
This is done in order to be able to build a chain of further actions in the case of successful or unsuccessful completion of the task.
')
var p = new Promise(function(resolve, reject) { someAsycOperation(function(e, result) { if (e) { reject(e); } else { resolve(result); } }); }); 


About then
Each promise has a then method that takes, as arguments, two functions (then-callbacks).
The first, it is also called onFulfilled callback - will be executed if the task is successfully completed, and the second, it is also called onRejected callback - in the event of a task failure.
But until we report the status of the task, none of these two functions will be called.
The then method returns another promise that can also be resolved or rejected and which can also be hung then.
And so on the circle ...

If, by calling resolve or reject, you pass some argument, then this argument will be forwarded to the next then-callback, which will be executed after the current task is completed.

 var p = new Promise(function(resolve, reject) { someAsyncOperation(function(e, result) { if (e) { reject(e); } else { resolve(result); } }); }).then(function(value) { //on success .... }, function(e) { //on fail console.error(e); }); 


Then-callback can also forward some value to the next then-callback, simply returning it using the return statement;

 var p = new Promise(function(resolve, reject) { someAsyncOperation(function(e, result) { if (e) { reject(e); } else { resolve(result); } }); }).then(function(value) { //on success ... return 'some value'; }, function(e) { //on fail console.error(e); }).then(function(value) { //value === 'some value'       }); 


About states
Inside the starting function, we have only 3 ways to report the result of the task.
Successful:


Failed:


For functions inside then, there is a small rule:


In other words: if the then-callback throws an exception or returns another promise that will be in the rejected state, then the whole promise will go to the rejected status, in all other cases, the promise will be in the resolved status (even if the then-callback returns nothing).
Look at the rule for then-callback, and then at the last example ...

It turns out that if the asynchronous operation is completed successfully, the onFulfilled callback will be executed in the first then, and then onFulfilled callback in the second then.
But what if an asynchronous operation fails? The onRejected callback will be executed in the first then, and then ( attention! ) OnFulfilled callback in the second then.
Why? See above rule for then-callback.
Proceeding from it, in order to call the next onRejected callback (which is by the way not), it is necessary: ​​either to return a promise that will be rejected, or to throw an exception.

By the way, if somehow it happened that you only need to hang on the promise onRejected callback, that is, the catch () shorthand method

 var p = new Promise(function(resolve, reject) { console.log('  '); someAsyncOperation(function(e, result) { if (e) { reject(e); } else { resolve(result); } }); }).catch(function(e) { console.log('  '); console.error(e); }).then(function() { console.log('  !'); }); 


Or, if you use then, instead of onFulfilled-callback, you can simply pass null.

But back to the topic ... see what happens ...
onRejected callback in the first then plays the role of such a fallback-action. Then, the then chain is executed further, as if the asynchronous operation were completed successfully.

 var p = new Promise(function(resolve, reject) { console.log('  '); someAsyncOperation(function(e, result) { if (e) { reject(e); } else { resolve(result); } }); }).then(function(result) { console.log('   '); }, function(e) { console.log('  '); console.error(e); }).then(function() { console.log('  !'); }); 


It turns out that promises in JS already support out-of-the-box fallback actions.

And now, as promised, I give a link to the preloader of pictures , which implements the possibility of replacing the broken pictures with the default pictures.
The principle of its work, I just described.

By the way, the preloader uses the allSettled-function, whose work is based on the same principle of fallback-actions.
I also recommend to get acquainted with the code .

Thanks for attention!

PS do not be lazy - read the documentation on promises !

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


All Articles