0

I have an array of functions that I need to execute. Those functions all return a promise.

I want to run all the functions in series, but the next function can only be started if the promise of the previous function is done.

I thought this would be easy with the async or bluebird library, but I can't find a simple solution to this.

Here is something I made (untested), but I was looking for a standard library solution because this probably already exists?

function runFuncs(funcs) {

    function funcRunner(funcs, resolve, reject, pos=0) {
        funcs[pos]().then(function() {
            pos++;
            if (pos < length(funcs)) {
                funcRunner(funcs, pos);
            } else {
                resolve();
            }
        }).catch(function(err) {
            reject(err);
        });
    }

    return new Promise(function(resolve, reject) {
        funcRunner(funcs, resolve, reject);
    });
}
roeland
  • 6,058
  • 7
  • 50
  • 67
  • Can you include `js` that you have tried at Question? And describe what expected and actual result are? – guest271314 May 06 '16 at 20:21
  • ^^^^ need some code in order to help you. what @guest271314 said – Sitian Liu May 06 '16 at 20:29
  • @r03 _"Here is something I made (untested), but I was looking for a standard library solution because this probably already exists?"_ Not certain what Question is? Are you seeking a recommendation for a library that uses a pattern similar to `js` at Question? Can you include `js` contained in `funcs` array at Question? – guest271314 May 06 '16 at 20:36
  • Potential answer here: http://stackoverflow.com/questions/24586110/resolve-promises-one-after-another-i-e-in-sequence – Tom May 06 '16 at 20:37

4 Answers4

4

If each of the functions returns a promise itself, you can just chain them together. Something like this should work:

function runFuncs(funcs) {
  return funcs.reduce(function (p, funcToRun) {
      return p.then(function () {
        return funcToRun();
      });
  }, Promise.resolve(null));
}

Basically, you just keep chaining the promises together. By the nature of the chain, if you return a promise from the then handler, that becomes the resulting promise. So the reduce call goes through each promise in the array, doing a then on it to process the next one in the array.

Chris Tavares
  • 29,165
  • 4
  • 46
  • 63
1

I know you've marked an answer already, but I can't comment yet and had to ask: Your example doesn't seem to pass results from one function to another, do you really need them to run in series?

If series is a hard requirement, Bluebird's .mapSeries() method will do exactly what you're looking for, and the code is incredibly neat. Otherwise you can swap in .map() and leverage concurrency to get things done in parallel.

Promise.mapSeries(fnArray, (fn) => 
{
    return fn(...);
})
.then((results) =>
{
    // do something with the results.
});

Or in parallel with .map():

Promise.map(fnArray, (fn) => 
{
    return fn(...);
}, 
{ concurrency: 5 })
.then((results) =>
{
    // do something with the results.
});
roeland
  • 6,058
  • 7
  • 50
  • 67
0

Using no additional libraries, just plain old JS code, I was able to come up with a simple solution.

'use strict';

// Create an array of functions that return a promise
const promises = [];
let i = 0;
while (i++ < 10) {
  const v = i;
  promises.push(() => {
    return new Promise( (resolve, reject) => {
      setTimeout(() => {
        resolve(v);
      }, Math.floor(Math.random() * 1000));
    });
  });
}

function nextPromise(idx) {
  if (idx >= promises.length) return;
  promises[idx]().then((v) => {
    console.log("Resolved", v);
    nextPromise(++idx);
  });  
}

nextPromise(0);

In this example, I created an array of 10 functions, each of which returns a promise which resolves a monotonically increasing number after random number of milliseconds.

To execute them all sequentially, I just use some recursion. It's kind of an ugly bit of hackey looking code, but it works.

lanceball
  • 595
  • 4
  • 9
0

If "funcs" is array of promises, you can to use Promise.all() - Promise docs

import {Promise} from 'es6-promise';

const runFuncs = funcs => {
  return Promise.all(funcs);
};

export default runFuncs;