1

There are many answers for this question, but most of them synchronize the resolution of the promises (i.e. the end of execution, not the beginning of execution).

This solution assumes we already have an array of functions:

var funcs = [foo, bar, baz, qux];

How is such array created without executing the functions? I have tried this:

var promises = [];
for (i = 0 ; i < 3, ++i){
    promises.push( someFunction(i) )
}

function someFunction(i) {
    return new Promise((resolve, reject) => {
        console.log(i);
        resolve(i);
    });
}

By the end of for loop the promises array is populated, but someFunction is already executed 4 times. Using Promise.all or Q sequences just sequences resolves.

How can I achieve the true synchronization of beginning of these functions? In particular:

foo().then(bar).then(baz).then(qux);

----------------------------------------------------------------

Update (Simple Solution using Async Functions): The simple way to do this is to use async functions and await. See this answer for an example of calling the promise in a loop, where each promise will be called when the previous one is resolved.

Ari
  • 7,251
  • 11
  • 40
  • 70
  • Check this answer: https://stackoverflow.com/a/44371402/7636961 – Egor Stambakio Jun 14 '17 at 08:12
  • I don't think the built in promise API has this feature. You could try Promise.reduce by bluebird: http://bluebirdjs.com/docs/api/promise.reduce.html – Mμ. Jun 14 '17 at 08:12
  • @KarelG: exactly my question! How can I push it into array without calling it? I want to create these promises in a for loop on the fly. – Ari Jun 14 '17 at 08:16
  • `for (let i = 0 ; i < 3, ++i){ funcs.push(() => someFunction(i)) }` will do. – Bergi Apr 19 '19 at 17:42
  • @Bergi: I don't think this will run them serially where one waits until the previous one is resolved. – Ari Apr 19 '19 at 18:50
  • @Ari It doesn't run anything, it creates the array of functions that the solution you found needs. – Bergi Apr 19 '19 at 21:57

2 Answers2

1

The problem is

promises.push( someFunction(i) )

when you're populating the promises array, the someFunction is being called each time before its value is passed to the .push method. So it executes earlier as you want.

A work around is to not call it. That's not easy to implement, so I was thinking at a little (but ugly) hack. I have introduced an object {p: ..., val: ...} which contains a promise function and value(s) for that promise function. If you want to provide multiple values, just pass an array. The apply() method will convert it to your function arguments. The function callPromise handles the execution of the function in a correct manner.

It can be that someone comes up with a better solution, but that's one I could think of quickly.

Here below is an example where the promises are called in sequence. I've added an extra function (in case if you have another function has more than one argument) as a PoC.

var promises = [];
for (let i = 0 ; i < 3; ++i){
    promises.push( {p: someFunction, val: i} );
}

promises.push({p: anotherFunction, val: [100,33]});
promises.push( {p: someFunction, val: 4} );

function someFunction(i) {
    return new Promise((resolve, reject) => {
        console.log('someFunction, i: ' + i);
        resolve(i);
    });
}

function anotherFunction(i1, i2) {
    return new Promise((resolve, reject) => {
        console.log('anotherFunction, i1: ' + i1 + ', i2: ' + i2);
        resolve(i);
    });
}


function callPromise(prom) {
    if(Object.prototype.toString.call(prom.val) !== '[object Array]') prom.val = [prom.val];
    return prom.p.apply(null, prom.val);
}

console.log('before loop');
let result = callPromise(promises[0]);
for (let i = 1 ; i < promises.length; ++i) {
  result = result.then(callPromise(promises[i]));
}
KarelG
  • 5,176
  • 4
  • 33
  • 49
  • Can you please explain what exactly callPromise is doing? – Ari Jun 14 '17 at 14:48
  • @Ari `prom` is the `{p: ..., val: ...}` object. It checks first if val is an array. If not, make it an array. Then it runs the function `p` with the val array by using `.apply`. If you check its doc, you will find out that the array is supplied as argument to the function p. That function returns with a promise which then can be handled like a promise: with `.then` and `.catch` ect ... – KarelG Jun 14 '17 at 15:09
0

You need to pass only the name of the function, without passing parameters. In your example you call

      someFunction(i) 

- you pass the parameter i, so this is a function call. You should push the functions by their name only. The parameter for each function is the value which was resolved in the previous promise.

function someFunction(i) {
    return new Promise((resolve, reject) => {
        console.log(i);
       resolve(i+1); // increase the value here
   });
}

In this way the parameter will be passed to the next function increased by 1. But for the first function in the chain you should pass the parameter. Something like

 someFunction(0).then(someFunction).then(someFunction)

(In this solution you don't need the array.)

Cz01
  • 68
  • 7
  • So how can I call these functions correctly in a sequence later? – Ari Jun 14 '17 at 08:39
  • @Ari You want to have the same function executed 3 times? – Cz01 Jun 14 '17 at 08:43
  • Yes, with different parameters. – Ari Jun 14 '17 at 08:45
  • @Ari Each function in the chain shouldbr responsible of passing the parameter to the next one by resolving the value. The first one you can call with the parameter you want to start with. see my edit – Cz01 Jun 14 '17 at 08:55