How to chain execution of array of functions when every function returns deferred.promise?

25,048

Solution 1

You need to build a promise chain in a loop:

var promise = funcs[0](input);
for (var i = 1; i < funcs.length; i++)
    promise = promise.then(funcs[i]);

Solution 2

Same idea, but you may find it slightly classier or more compact:

funcs.reduce((prev, cur) => prev.then(cur), starting_promise);

If you have no specific starting_promise you want to use, just use Promise.resolve().

Solution 3

ES7 way in 2017. http://plnkr.co/edit/UP0rhD?p=preview

  async function runPromisesInSequence(promises) {
    for (let promise of promises) {
      console.log(await promise());
    }
  }

This will execute the given functions sequentially(one by one), not in parallel. The parameter promises is a collection of functions(NOT Promises), which return Promise.

Solution 4

Building on @torazaburo, we can also add an 'unhappy path'

funcs.reduce(function(prev, cur) {
  return prev.then(cur).catch(cur().reject);
}, starting_promise); 

Solution 5

ES6, allowing for additional arguments:

function chain(callbacks, initial, ...extraArgs) {
 return callbacks.reduce((prev, next) => {
   return prev.then((value) => next(value, ...extraArgs));
 }, Promise.resolve(initial));
}
Share:
25,048

Related videos on Youtube

PaolaJ.
Author by

PaolaJ.

Updated on July 09, 2022

Comments

  • PaolaJ.
    PaolaJ. almost 2 years

    I have created my first deferred object in Node.js using deferred module and it works great when I pass result to next function and trigger resolve and reject.How to chain execution of array of functions when every function returns deferred.promise ? I have like input parameters array of functions and input parameter for first function and every next function get parameter from previous.

    It works like f1(100).then(f2).then(f3), but how when I have n number of functions.

    • lonewarrior556
      lonewarrior556 over 8 years
      Totally just googled your question word for word
  • Beetroot-Beetroot
    Beetroot-Beetroot over 10 years
    Yes, this is what I call "chaining by assignment" - not an official term but hopefully meaningful.
  • jchook
    jchook over 8 years
    This solution is fundamentally stronger than @SLaks' solution because of the closure's ability to reference the current executing callback. In the case where you want to "do extra async stuff" within the chain, funcs[i] will not help you but cur will.
  • saraf
    saraf about 8 years
    How to invoke a callback after the last promise has been resolved?
  • SLaks
    SLaks about 8 years
    @saraf: Call .then() on it, just like any other promise.
  • Popara
    Popara almost 8 years
    Reducing the chain of promises with this technique is really smooth way to think about what is going to happen you your code.
  • Popara
    Popara almost 8 years
    Iterating through array with for statement is less classy than using reducing technique. But works!
  • Dmytro
    Dmytro almost 8 years
    @Popara I just spent an hour trying to wrap my head around doing this same thing via reduction. And I still can't find a way to express it using reduction... If somebody expresses what SLAks did using reduction i'd appreciate it but until then I'll have to stick to this approach.
  • Dmytro
    Dmytro almost 8 years
    Actually, I got it, reduce takes the chain as well as the "identity condition". I kept passing the "identity" promise as the second argument instead of "identity()", so it kept saying that "prev is not a function". Here is my snippet overviewing a job demo which "first get all checkboxes" "then uncheck them one by one" "then check them one by one". hastebin.com/ixojikoxem.coffee
  • SLaks
    SLaks almost 8 years
    @Dmitry: That parameter is actually just the initial value.
  • Dmytro
    Dmytro almost 8 years
    @SLaks yeah but if you don't specify it or specify a promise as the argument instead of a literal, then it won't resolve the initial promise for you(you have to invoke it first) and cause an error which is not obvious to people unaware of what reduce does, and what that second argument means.. Basically the non obvious part is that YOU have to resolve the first step yourself, after which it pipelines nicely.
  • Admin
    Admin over 7 years
    @Kumagoro The prev.then(cur) is correct, because remember cur is a function which returns a promise.
  • 3limin4t0r
    3limin4t0r almost 4 years
    Be careful with var here. The current solution might work, but if you are passing a lambda to then that uses i you are in for a surprise.