Search code examples
javascriptnode.jspromisees6-promise

Sequential iteration using ES6 promises


I was expecting that below node.js code will print output in following order

1000
2000
3000
4000
"All tasks completed"

Instead it is prints in below mentioned order

"All tasks completed"
1000
2000
3000
4000

The code

'use strict';

var tasks = [1000, 2000, 3000, 4000];
var promise = Promise.resolve();


function test() {
  tasks.forEach(function(task) {
    promise = promise.then(function() {
      setTimeout(function() {
        console.log(task);
      }, task);
    });
  });
}

test();

promise.then(function() {
  console.log('All tasks completed');
});

What needs to be modified so that "All tasks completed" will be printed last.

  1. My example uses ES6 promises and not bluebird .
  2. Also I am not asking a general question about promises , rather it is about a specific example.

Solution

  • In all your then functions you are not returning anything, but triggering an async operation. So, the promise chain has nothing to do with the async operations. That is why you are not able to control the flow as you wished.

    What you could have done is, return a Promise from each of the then handlers, which will be resolved only when the async operation completes, like this

      tasks.forEach(function(task) {
        promise = promise.then(function() {
          return new Promise((resolve, reject) => {
            setTimeout(function() {
              console.log(task);
              resolve();
            }, task);
          })
        });
      });
    

    Remember, this will trigger the async operations one by one. For example, after one second, it will print 1000 and the second async operation will be started which will wait for two seconds and then print 2000 and so on. Basically, you program will exit after approximately 10 seconds (1 + 2 + 3 + 4 seconds), as we are executing all the asynchronous functions sequentially.


    But if you wanted all of them to trigger at once, then use Promise.all, like this

    'use strict';
    
    var tasks = [1000, 2000, 3000, 4000];
    
    function test() {
      return Promise.all(tasks.map(function(task) {
        return new Promise((resolve, reject) => {
          setTimeout(function() {
            console.log(task);
            resolve();
          }, task);
        })
      }));
    }
    
    test().then(function() {
      console.log('All tasks completed');
    });
    

    Now, all the async functions are triggered at once, so after one second, 1000 will be printed, after two seconds 2000 will be printed and so on. Your program will complete all the async operations after 4 seconds, as all of them are started immediately.