Ivan Wang
Ivan Wang

Reputation: 8445

Execute an array of Javascript promises one after one resolved

I wonder if there is a way that I can execute an array of promises waterfally? i.e. I want the next promise not to start until the current promise is resolved.

My test:

import Promise from 'bluebird'

describe('promises waterfall', () => {
  const result = []
  function doItLater(val, time = 50) => {
    return new Promise(resolve => {
      setTimeout(() => {
        result.push(val)
        resolve(val)
      }, time)
    })
  }

  it('execute promises one after one resolved', done => {
    result.push(1)
    const promiseList = [doItLater('a', 100),doItLater('b', 1),doItLater('c')]
    result.push(2)

    Promise.each(
      promiseList,
      (output) => {
        result.push(output + '-outputted')
      }
    )
      .then(
        () => {
          result.push(3)
          console.log(result)
          expect(result).to.eql([ 1, 2, 'a', 'b', 'c', 'a-outputted', 'b-outputted', 'c-outputted', 3 ])
          done()
        }
      )
  })
})

Update

Sorry if I have made it too confusing. I'm asking how I can make my test pass. Currently my test fails - the actual result is in wrong order:

[ 1,
  2,
  'b',
  'c',
  'a',
  'a-outputted',
  'b-outputted',
  'c-outputted',
  3 ]

Upvotes: 2

Views: 1057

Answers (2)

Cleriston
Cleriston

Reputation: 770

// Promisse waterfall pattern
var promises = [1,2,3].map((guid)=>{
    return (param)=> {
      console.log("param", param);
      var id = guid;
      return new Promise(resolve => {
        // resolve in a random amount of time
        setTimeout(function () {
          resolve(id);
        }, (Math.random() * 1.5 | 0) * 1000);
      });
    }
}).reduce(function (acc, curr, index) {
  return acc.then(function (res) {
    return curr(res[index-1]).then(function (result) {
      console.log("result", result);
      res.push(result);
      return res;
    });
  });
}, Promise.resolve([]));
promises.then(console.log);

Upvotes: 0

thefourtheye
thefourtheye

Reputation: 239573

When you create promises like this

[doItLater('a', 100),doItLater('b', 1),doItLater('c')]

they are already executing asynchronously. There is no way of controlling their order of execution.

You can use Promise.reduce for your case, like this

Promise.reduce([['a', 100], ['b', 1], ['c']], function(res, args) {
    return doItLater.apply(null, args).then(function(output) {
        res.push(output + '-outputted');
        return res;
    });
}, result)
.then(function(accumulatedResult) {
    ....
});

Now, we are creating promises one by one, after the previous promise is resolved and we are accumulating the result in res (which is actually result). When all the promises are resolved, the accumulatedResult will have all the values.


Note: It is strongly recommended NOT to share data between functions. Please make sure that you have compelling reasons to do so, if you have to do.

Upvotes: 2

Related Questions