1

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.
refactor
  • 13,954
  • 24
  • 68
  • 103
  • 1
    Your nested callbacks are not promises that `resolve`. Modify your anonymous function to return a new `Promise` that resolves after a timeout. – zerkms Jun 20 '16 at 06:51
  • Possible duplicate of [resolving an array of promises from within a parent promise](http://stackoverflow.com/questions/37081508/resolving-an-array-of-promises-from-within-a-parent-promise) – jib Jun 20 '16 at 15:09

2 Answers2

5

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.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
0

Currently, this is the only method I figured out to iterate over a promise array within a sequential AND blocking manner...

Please check the code example...

const list = [1,2,3,4,5];

The higher the number the faster the promise gets resolved

const functionWithPromise = item => { 
  return new Promise((resolve) =>{
    setTimeout(resolve, 6000 - (1000 * item ) , item);
})}

Promise.all returns an array with promises

const getData = async () => Promise.all(await iterateSequentiallyPromiseArray(list,functionWithPromise));

The for loop is the only loop that iterate in a blocking manner

const iterateSequentiallyPromiseArray = async (array, fn) => {
    try {
      const results = [];
      for (let i = 0; i < array.length; i++) { 
        console.log('Start with index: ', i);
        const r = await fn(array[i]);
        console.log('in promise iteration', r);
        results.push(r);
      }
      return results; // will be resolved value of promise
    } catch (err) {
      console.log('_processArray');
      throw err;
    }

};

Start the chain

getData().then(console.log);
Patrick W.
  • 149
  • 1
  • 6