There is a code that is taken from here :

var f1 = function() { return new Promise(function(resolve) { setTimeout(function() { console.log(1); resolve(); }, 1000); }); } var f2 = function() { return new Promise(function(resolve) { setTimeout(function() { console.log(2); resolve(); }, 2000); }); } var f3 = function() { return new Promise(function(resolve) { setTimeout(function() { console.log(3); resolve(); }, 3000); }); } var seqRunner = function(deeds) { return deeds.reduce(function(p, deed) { return p.then(deed); }, Promise.resolve()); } seqRunner([f1, f2, f3]).then(function() { console.log('Done!'); });; 

Explain to me, please, I don’t understand at all: why and for what purpose should the seqRunner function return an empty promise?
It is clear that after all the deeds have been executed, it calls Promise.resolve() to execute the external .then with the console output command Done! . It's clear. But I thought that if I clean at the end

 .then(function() { console.log('Done!'); });; 

Also , Promise.resolve()) , the queue will also work without errors.
God knows, I don't understand promises. Why this , Promise.resolve()) in returning the seqRunner function?
Why can not do this:

 var seqRunner = function(deeds) { return deeds.reduce(function(p, deed) { return p.then(deed); }; } seqRunner([f1, f2, f3]); 
  • 2
    Promise.resolve () is the starting point for building an array of promises. In general, as you suggest, you cannot do it, because promises are stored in the array, and the first parameter in then must be a function. Although not, in this case in the array of the function, but in this case - the functions do not have the then method - Grundy
  • @Grundy, not, not like that. The first parameter should be a promise, not a function. I understood , but because of my carelessness I got into a stupor. Instruction , Promise.resolve()); is not in the return function of the seqRunner , but is passed as the second parameter to reduce . And the second parameter for it is the initial value for the previous (first parameter, p ) parameter. Thus, such a chain is obtained. The first promis is immediately resolved, so that the next one can already be "hung" on it ... Is that how I understood everything? - VostokSisters
  • :-D now I do not understand - Grundy
  • By the way, in the answer to which you refer there is an unequivocal comment "We initialize the execution queue " just next to Promise.resolve() . - Dmitriy Simushev
  • @DmitriySimushev, all these comments are dust, when you just did not notice the bracket and think that Promise.resolve() refers to another (: - VostokSisters

2 answers 2

You think that the second argument to the reduce function is the last promise in a chain. If you look at what this function does , you will see that this is the first promise, the initial value!

The construction of [f1, f2, f3].reduce((p, deed) => p.then(deed), Promise.resolve()) in the process of execution, you open it up in this:

 Promise.resolve().then(f1).then(f2).then(f3) 

Removing the initial value from here is not so easy. If you are going to write a call chain with your hands, you probably write like this:

 f1().then(f2).then(f3) 

Please note: the sequence is now processed non-uniformly. To achieve the same effect through reduce over an array, you must first cut off the first element from it and process it separately:

 var seqRunner = function(deeds) { var first = deeds.shift(); return deeds.reduce(function(p, deed) { return p.then(deed); }, first()); } 

As you can see, the code did not become simpler, rather became more complicated. And he became much worse because of this - bugs appeared in it:

  1. if the call to f1() throws an exception, then the old code caught it and saved it in the same way as for any other method. New code exception from f1 will throw further down the call stack;
  2. the modified code changes the array passed to it. For calls of the form seqRunner([f1, f2, f3]) this is not a problem - but if you call it over a stored array, there will be a surprise. Working without changing the array will be even more difficult;
  3. the old code returned Promise.resolve() when called on an empty array, but the new one requires at least 1 element.

PS here is the correct code without these bugs in a fresh syntax:

 var seqRunner = function([first, ...tail]) { if (first === undefined) return Promise.resolve(); try { return tail.reduce((p, deed) => p.then(deed), first()); } catch (e) { return Promise.reject(e); } } 

As you can see, starting through Promise.resolve() is much easier.


The choice of an “empty” initial value when traversing an array with an accumulation of results is a general rule. So, when summing up the array members, accumulate the sum starting from 0, when searching for the minimum number in the array, start from infinity, and the sequential execution of asynchronous methods starts from Promise.resolve() . Because it's easier and more beautiful.

  • I just wrote about this in the commentary question ((: I figured it out. I thought it was the return of the seqRunner function that’s that and not the second parameter reduce , so I asked the question. But this is just inattention. - VostokSisters
  • disagree with the third item: shift On an empty array, returns undefine, which is transferred as a battery, and there will be no error — it will simply return an empty array. And, stop, yes, you're right, when you call a function, everything will fall. Sadly - Grundy

All your conclusions are built from the wrong premise:

It is clear that after all the deeds have been executed, it calls Promise.resolve() to execute the external .then with the console output command Done! . It's clear.

This is not true.

By Array.prototype.reduce second argument to the Array.prototype.reduce method, you use its value as the initial value of the accumulator. Here is a simple example:

 var sum = [1, 1, 1].reduce(function (acc, val) { return acc + val; }, 10); console.log(sum); // 13 

In your case, you need to set the initial value in Promise.resolve to initiate the promise chain.

Design:

 seqRunner([f1, f2, f3]).then(function() { console.log('Done!'); }); 

is equivalent to

 Promise.resolve() .then(f1) .then(f2) .then(f3) .then(function () { console.log('Done!'); }); 

As a consequence, using something other than the Promise at the beginning of the chain will make it invalid.

  • Thanks for the help, it's just my carelessness (: - VostokSisters