Search code examples
javascriptpromiselodash

Display results of all promises with order


I am new to promises.

I encountered a case where I have to loop through a series of tasks and get data. For some reason I have to do it in order and have to do it using promises. But to my surprise, the result only has the last element instead of all elements.

The simplified version of code

const _ = require('lodash');

const test = (name) => {
  return new Promise(function(resolve, reject) {
    //This is simplified from my actual code
    resolve(name);
  });
};

const testAll = (arr) => {
  //This chains and returns a series of functions with the order of arr, so that the can be executed in the order, not simultaneously
  let functions = _.map(arr, element => test.bind(null, element));
  //This reduces the functions and is supposed to return an array of all values
  return functions.reduce((fun1, fun2) => fun1.then(fun2), Promise.resolve([]));
}

const arr = ['promise1', 'promise2', 'promise3'];

testAll(arr)
.then(data => console.log(data));

I was expecting the output to be (with order):

promise1 
promise2
promise3

but what I really got was just promise3. Is it because the Promise.resolve([]) does not include every element in an array?


Solution

  • You seem to want to accumulate the individual results into an array. For that you must at least capture the resolved value from each of the promises. When you do fun1.then(fun2) you throw away the value promised by fun1. To use it, you need to do something with the argument that is passed to the then callback. In your case you want to concatenate that with the promised value of fun2().

    But since you have the first, but must still wait for the second, you could benefit from Promise.all, like this:

    const testAll = (arr) => {
      let functions = arr.map(element => test.bind(null, element));
      return functions.reduce((prom, fun) => 
          prom.then(data => Promise.all(data.concat(fun())) ),
          Promise.resolve([])
      );
    }
    

    Now your final call to testAll will give you an array as result.

    const test = (name) => {
        return new Promise(function(resolve, reject) {
            setTimeout(_ => { // Introduce delay for better demo
                console.log('.'); // a little trace in the output
                resolve(name); 
            }, 500);
        });
    };
    
    const testAll = (arr) => {
      let functions = arr.map(element => test.bind(null, element));
      return functions.reduce((prom, fun) => 
          prom.then(data => Promise.all(data.concat(fun())) ),
          Promise.resolve([])
      );
    }
    
    const arr = ['promise1', 'promise2', 'promise3'];
    
    testAll(arr).then(data => console.log(data));