1

How can I dynamically create a series of promises and have them execute in sequence?

pseudocode
 for x=0 to maxValue
   promiseArray.push(createNewPromise(x))

 executeAllPromisesSequentially(promiseArray)

where

executeAllPromisesSequentially is functionally equivalent to

promise1()
.then(promise2)
.then(promise3)
etc
...
takinola
  • 1,643
  • 1
  • 12
  • 23
  • 1
    yes, you can do that by chaining the promises you create in the loop ... `.push(promise = promise.then(() => createNewPromise(x)))` - of course, you need an initial value of `promise` to start the chain ... say `Promsie.resolve()` for example ... it gets a little more complicated if you want to access the resolved values – Jaromanda X Apr 06 '18 at 05:33
  • Do all the promises only have a side effect or would you like to know the resolve value of them? – HMR Apr 06 '18 at 05:40
  • *have them execute in sequence?* You meant **resolve** them in sequence? since `promise` will be executed the moment it is created. – gurvinder372 Apr 06 '18 at 05:46
  • Check this one if it suits your requirement https://stackoverflow.com/questions/49606387/how-to-write-core-logic-waterfall-callback-for-array-of-data-in-javascript-or/49606880#49606880 – gurvinder372 Apr 06 '18 at 05:48
  • Possible duplicate of [Resolve promises one after another (i.e. in sequence)?](https://stackoverflow.com/questions/24586110/resolve-promises-one-after-another-i-e-in-sequence) – Redu Apr 09 '18 at 02:31

3 Answers3

2

There are some patterns displayed on my gist

Promise Iteration with Reduce

let tasks = [ /* ... */ ]
let promise = tasks.reduce((prev, task) => {
  return prev.then(() => {
    return task();
  });
}, Promise.resolve());
promise.then(() => {
//All tasks completed
});

Sequential Iteration Pattern

let tasks = [ /* ... */ ]
let promise = Promise.resolve();
tasks.forEach(task => {
  promise = promise.then(() => {
    return task();
  });
});
promise.then(() => {
//All tasks completed
});

Sequential Iteration Example

function spiderLinks(currentUrl, body, nesting) {
  let promise = Promise.resolve();
  if(nesting === 0) {
    return promise;
  }
  const links = utilities.getPageLinks(currentUrl, body);
  links.forEach(link => {
    promise = promise.then(() => spider(link, nesting - 1));
  });
  return promise;
}
Muhammad Faizan
  • 1,709
  • 1
  • 15
  • 37
1

Just build up a chain as jaromandaX said. However you need to make sure that you use let inside the loop to closure the x:

  let chain = Promise.resolve();
  const promises = [];

  for(let x = 0; x < maxValue; x++)
    promises.push(chain = chain.then(() => createNewPromise(x)));
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
0

Reducing or loop / recursion chaining comes to mind as a common practice however if you would like to keep and access to the intermediate resolutions here i have another approach by using an invention of Haskell's scanl function in JS.

scanl is similar to JS .reduce() but like .map() always returns a same size array holding the interim values. So the scanl function would look something like;

var scanl = (xs, f, acc) => xs.map((a => e => a = f(a,e))(acc));

So if you do;

scanl([1,2,3,4], (a,e) => a + e, 0) // -> [1,3,6,10]

So having scanl at hand now we may attempt to sequence promises by keeping the intermetiate resolutions in a resulting array.

var scanl = (xs, f, acc) => xs.map((a => e => a = f(a,e))(acc)),
    proms = Array(5).fill().map((_,i) => new Promise((v,x) => setTimeout(v,100+Math.random()*1900,`res ${i+1}`)));
  
proms = scanl(proms, (a,p,t) => a.then(v => (t = v, p))
                                 .then(v => `${t} and ${v}`)
                                 .then(s => (console.log(`did stg with ${s}`),s)), Promise.resolve("init 0"));
Promise.all(proms)
       .then(vs => vs.forEach(v => console.log(v)));
.as-console-wrapper {
max-height : 100% !important
}

Of course above function is just makeshift. I use an unused t argument as a temporary variable defined in the callbacks context.

Redu
  • 25,060
  • 6
  • 56
  • 76