Search code examples
javascriptes6-promise

How do I serialize a Javascript array of functions returning Promises?


My question is very similar to this, but I can't get it to work :(

PROBLEM:

  1. I have a Javascript program that needs to run in IE11 and Chrome.

  2. It has a list of functions that I need to execute in order.

  3. Each function returns a Promise. Promise.all(promisesArray) works to the extent that I can "wait" until all the functions finish before proceeding. But it DOESN'T guarantee that the functions each run in order. This is essential.

  4. I've tried using Array.prototype.reduce(), but I haven't been able to figure out how to use it correctly.

  5. Because I need to run in IE11, I can't use ES6 features like "arrow functions".

Here is my code:

<script>
var fn = function(arg) {
  return new Promise (function (resolve, reject) {
    console.log("fn(" + arg + ")...");
    resolve("OK");
  });
}

var p = fn("A").then(function(r) {
  console.log("promise resolved: " + r + ".");
})

var chain = [];
chain.push(fn("3"));
chain.push(fn("1"));
chain.push(fn("2"));
console.log("Built chain:", chain);
Promise.all(chain);

chain.length = 0;
chain[2] = fn("30");
chain[1] = fn("20");
chain[0] = fn("10");
chain.reduce(function(promise, item) {
  return promise.then(function() {
    console.log("then:", item);
  }), Promise.resolve();
});
console.log("Done.");
</script>;

I need the functions to execute in order array[0], array[1], array[2].


Solution

  • You're really close with your reducer!

    The initial value for the reducer is Promise.resolve(), so when the function is called the first time:

    chain.reduce(function(promise, item) {
    //                    ^^ promise is a resolved promise,
    //                             ^^ item is chain[0]
      return promise.then(function() {
        console.log("then:", item);
        //           ^^ when the promise resolves (immediately) this is called
        //              BUT no value is returned.
      }), Promise.resolve();
    

    });

    Compare this with manually chaining the promises. You'd return the next promise to wait for:

    Promise.resolve()
      .then(item => { console.log("then: ", item); return fn("10"); })
      .then(item => { console.log("then: ", item); return fn("20"); })
      .then(item => { console.log("then: ", item); return fn("30"); })
    

    See how the reducer is so close? We'll just want to return a promise:

    var chain = [fn("10"), fn("20"), fn("30")];
    
    chain.reduce(function(promise, item) {
      return promise.then(function() {
        console.log("then:", item);
        return item;
      }), Promise.resolve();
    });
    

    Edit:
    If the fn call starts the work, each of these assignments will start the calls out of order:

    chain.length = 0;
    chain[2] = fn("30");
    chain[1] = fn("20");
    chain[0] = fn("10");
    

    To run each fn in the order you want, you'll have to defer the call to fn until the previous call resolves. We did that in our manual example above. Depending on how complex your data is, you could reduce over the array of arguments for fn or wrap each call in a function that won't run:

    [10,20,30]
      .reduce(function(promise, arg) {
        return promise.then(function() {
          return fn(arg);
        }), Promise.resolve();
      });
    
    [function() { return fn(10); }, function() { return fn(20) }, function() { return fn(30) }]
      .reduce(function(promise, callFn) {
        return promise.then(function() {
          return fn(callFn);
        }), Promise.resolve();
      });