Search code examples
javascriptes6-promise

JavaScript Promises: Executing Promises Sequentially


In an attempt to understand promises more clearly, i have been reading up a few very interesting articles on the same. I came across the following code which works perfectly for executing promises sequentially. But i am not able to understand how it works.

 function doFirstThing(){
   return new Promise(function(resolve,reject){
       setTimeout(()=>{
           resolve(1);
       },1000)
   })
 }

 function doSecondThing(res){
   return new Promise(function(resolve,reject){
       setTimeout(()=>{
           resolve(res + 1);
       },1000)
   })
 }

 function doThirdThing(res){
   return new Promise(function(resolve,reject){
       setTimeout(()=>{
           resolve(res + 2);
       },1000)
   })
 }
 promiseFactories = [doFirstThing, doSecondThing, doThirdThing];

 function executeSequentially(promiseFactories) {
         var result = Promise.resolve(); // this is the most problematic line 
               promiseFactories.forEach(function (promiseFactory) {
               result = result.then(promiseFactory);// what is happening here ?
    });
    return result;
 }

 executeSequentially(promiseFactories)

I do understand that promises are executed as soon as they are created. For some reason i am not able to understand the flow of execution. Especially this following line:

var result = Promise.resolve()//and empty promise is created.

Please if somebody can help me understand how calling the promiseFactory method inside the 'then' method of the empty promise makes it execute sequentially, like so. Or is it because of the forEach loop ?

result = result.then(promiseFactory);

I tried replacing the 'forEach' with a 'map' function and still yielded the same result. i.e, the methods where executed sequentially. Also, how is the value passed from one chained function to other ?

Any help or article/blog is highly appreciated.


Solution

  • The executeSequentially method returns all the Promises one after each other. It happens to iterate over promiseFactory, but it could be written as:

    function executeSequentially(promiseFactories) {
      return doFirstThing()
      .then(() => doSecondThing())
      .then(doThirdThing() );
    }
    

    It is just the same. We are basically returning a Promise.

    Now, however, we want to iterate over a collection of promises.

    When iterating, we need to attach the current Promise to the previous with a then. But the forEach does not expose the next Promise -or the previous- in every iteration. And yet we still need it in order to keep chaining Promises one by one. Hence, the result 'hack':

    function executeSequentially(promiseFactories) {
      var result = Promise.resolve(); /*We need a thing that keeps yelling 
      the previous promise in every iteration, so we can keep chaining.
      This 'result' var is that thing. This is keeping a Promise in every
      iteration that resolves when all the previous promises resolve
      sequentially. Since we don't have a Promise in the array
      previous to the first one, we fabricate one out of 'thin air'
      with Promise.resolve() */
      promiseFactories.forEach(function (promiseFactory) {
        result = result.then(promiseFactory); /* Here result is update
        with a new Promise, with is the result of  chaining `result`
        with the current one. Since `result` already had all the previous ones,
        at the end, `result` will be a Promise that depends upon all the
        Promises resolution.*/
      });
    return result;
    }
    

    Now, there's also a syntax quirk that maybe is puzzling you:

    result = result.then(promiseFactory);
    

    This line is pretty much the same as the following:

    result = result.then(resolvedValue => promiseFactory(resolvedValue));
    

    Please if somebody can help me understand how calling the promiseFactory method inside the 'then' method of the empty promise makes it execute sequentially, like so. Or is it because of the forEach loop ?

    First thing first, promiseFactory is a pretty bad name there. The method should be better written as follows:

    function executeSequentially(promises) {
      var result = Promise.resolve(); // this is the most problematic line 
            promises.forEach(function (currentPromise) {
            result = result.then(currentPromise);// what is happening here ?
    });
    return result;
    }
    

    So:

    how calling the currentPromise method inside the 'then' method of the empty promise makes it execute sequentially?

    It makes execute sequentially because when you attach a Promise to another by then, it executes sequentially. Is a then thing, it is not at all related to the fact that we are iterating over Promises. With plain Promises outside an iteration it works pretty much the same:

    Promise.resolve() // fake Promises that resolves instanly
    .then(fetchUsersFromDatabase) // a function that returns a Promise and takes
    // like 1 second. It won't be called until the first one resolves
    .then(processUsersData) // another function that takes input from the first, and
    // do a lot of complex and asynchronous computations with data from the previous promise.
    // it won't be called until `fetchUsersFromDatabase()` resolves, that's what
    // `then()` does.
    .then(sendDataToClient); // another function that will never be called until
    // `processUsersData()` resolves