0

I'm trying to chain promises dynamically in order to deal with an unknown amount of asynchronous calls that need to happen in order. I'm using IO.JS/chrome which supports Promise's natively.

The creation of the promise fires immediately (at least relatively to console output). I was expecting to be able to collect promises then pass to Promise.all, but by that time they've already fired for reasons I don't understand.

Here's one method of chaining then, suggested by a comment on Dynamic Chaining in Javascript Promises

            var lastPr = null;
            console.log(" exit setup......................");
            while(this.statesToExit.length > 0) {


                var v = this.statesToExit.shift();
                console.log("new Promise...");
                var pr = new Promise(function (resolve, reject) {

                    console.log("doing Exit stuff at time " +curTime); 
                    resolve();  //SOMETHING MORE SUBSTANTIAL GOES HERE

                });

                console.log("lastPr.then.");
               if (lastPr != null) {
                    lastPr.then(pr);
                }
                lastPr = pr;
              //  console.log("adding pr to worklist");
               // promiseList.push(pr);
                // });
            }

The other approach is

            var promiseList= [];
            console.log(" exit setup......................");
            while(this.statesToExit.length > 0) {


                var v = this.statesToExit.shift();
                console.log("new Promise...");
                var pr = new Promise(function (resolve, reject) {

                    console.log("doing Exit stuff at time " +curTime); 
                    resolve();  //SOMETHING MORE SUBSTANTIAL GOES HERE

                });

                console.log("adding pr to worklist");
                promiseList.push(pr);
                 });
            }
 console.log("Transition *START*-" +promiseList.length +" ");
       Promise.all(promiseList).catch(function(error) {
            console.log("Part of TransitionCursor Failed!", error);
        }).then(this.summarizeWorkDone());

In both cases the output is like

new Promise...
doing Exit stuff at time 0
new Promise...
doing Exit stuff at time 0
    "Transition *START*-"

vs the expected

new Promise...
new Promise...
    "Transition *START*-"
doing Exit stuff at time 0
doing Exit stuff at time 0

How do I dynamically create a list of promises to later execute?

Community
  • 1
  • 1
TroyWorks
  • 411
  • 8
  • 16
  • Why does it matter when exactly the promise body is executed? Promises do not guarantee their body will be executed later. – zerkms Sep 12 '15 at 21:22
  • Maybe promise is not what you need here, seems like the execution order is important, so you would like to build some kind of massage queue and and then let it execute.. but in you example, what if there is only one item statesToExit? – webdeb Sep 12 '15 at 21:28
  • Your understanding of what promises do and how to use them looks to be pretty far off. We can help you restructure, but need to understand whether you want all the async actions to be run in serial (one after the other) or in parallel (all started at the same time)? We also need to see your actual async operation. The code in your question now is entirely synchronous and thus does not need promises. – jfriend00 Sep 12 '15 at 21:29
  • Thanks for the reply all, Zerkms, think of it like getting directions on a gps vs driving the actual directions. This particular task is oriented about getting the direction list, each element of which can take unknown time to complete (given traffic), and has a function before and after the set to setup and cleanup. A second phase is actually running.the list, which might happen immediately after or never. Webdeb, and Jfriend00. My apologies on the code not being good enough, I oversimplified, the real code is much longer and a bit odd to understand. – TroyWorks Sep 12 '15 at 22:29
  • enriched the answer I gave previously. check it out! – Tuhin Paul Mar 04 '18 at 01:31

3 Answers3

1

Ok,

So people are voting down my answer because I provided the external link, which in fact was written by me. So sad! So modifying the answer and the previous answer goes at the bottom of this answer.

Here, I shall focus some light on the problems on the first example because that's what I have explained in the link at the bottom of this post.

First of all: you are not chaining promises but creating multiple promises. When you create a promise, the function that you pass to the constructor gets called immediately. Check section 1 of the tutorial in the link below for details. That's why you get the output line 'doing Exit stuff at time ...' after 'new promise' line. You did not say anything about curTime but it seems that the values are not right although that's not the focus of this post.

Secondly, you should assign lastPr.then() to lastPr() but you are assigning the newly created pr to lastPr. This was explained in section 2 of the link below.

Thirdly, I would say that your expected output says that I should remind you that the function passed to promise constructor starts immediately. So you should not print anything during the promise creation and rather push them down in then() methods. However, I am not doing this here and so you will see that the first 'doing Exit stuff at time 0' line appears just after the first 'new Promise...' line.

Fourthly, you did not show 'lastPr.then.' in expected output

Note: I changed your code's curTime and statesToExit (removed 'this' reference and assigned test array)

I would suggest something like this:

var curTime = 0;
var lastPr = null;
console.log(" exit setup......................");
var statesToExit = [1, 2, 3]; // NOTE: the change to make this code runnable.
while (statesToExit.length > 0) {
    var v = statesToExit.shift();
    console.log("new Promise...");

    if (lastPr == null) {
        lastPr = new Promise(function (resolve, reject) {
            console.log("doing Exit stuff at time " + curTime);
            resolve();  //SOMETHING MORE SUBSTANTIAL GOES HERE
        });
    }
    else {

        console.log("lastPr.then."); // NOTE: This does not appear in your expected output
        lastPr = lastPr.then(result => {
            console.log("doing Exit stuff at time " + curTime);
            return Promise.resolve();  //SOMETHING MORE SUBSTANTIAL GOES HERE
        });
    }
    //  console.log("adding pr to worklist");
    // promiseList.push(pr);
    // });
}

// NOTE: now you need to esecute the chain:
// you should handle promise rejection:
// check the tutorial link:
// https://github.com/tuhinpaul/syp-model/wiki/Programmatic-Chaining-and-Recursive-Functions-with-JavaScript-Promise
lastPr.catch(err => { console.log(err); });

The output of the above code:

 exit setup......................
new Promise...
doing Exit stuff at time 0
new Promise...
lastPr.then.
new Promise...
lastPr.then.
doing Exit stuff at time 0
doing Exit stuff at time 0

I described dynamic promise chaining in the following tutorial: check the following tutorial for

  1. programmatic (dynamic) chaining of javascript/node.js promises and
  2. Promise chaining using recursive functions

Programmatic-Chaining-and-Recursive-Functions-with-JavaScript-Promise

Tuhin Paul
  • 558
  • 4
  • 11
  • Pointing to outside tutorials is not an appropriate answer here — this is why people are voting you down. You should actually answer the question in the body of the answer. – Mark Mar 02 '18 at 22:09
  • Thanks @Mark_M for notifying. It's disappointing to get -ve votes here as I have definitely addressed the issue of this page in my link. I just did not want to repeat the same kind of answer here. However, I will add to my answer. – Tuhin Paul Mar 04 '18 at 00:39
  • 1
    check it now, dude! – Tuhin Paul Mar 04 '18 at 01:32
-1

My understanding of promises was indeed off, as mentioned on this excellent page: http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html first they do start execution immediately, second to do dynamically you have to use a factory function to defer execution like so:

first create the chain start.

    var chain = Promise.resolve();
   //     chain.then(this.doTranStart);

then for each of the states (an array), then create a proxy closure so that the function can be called back with appropriate data. I'm not sure if there is something built into javascript for this, so I ported a library from actionscript 2.0, back when it had similar problems.

    this.statesToExit.forEach(function (state) {
            console.log("new Exit Promise Chain...");
              var pxfn = GetProxy.getProxy(self, self.doExitFactory, [curTime,state._fn,state]);
               chain = chain.then(pxfn);
            });

then later run the chain of factories, which will then call the factory and then execute the promise.

chain.catch(function(error) {
            console.log("Part of TransitionCursor Failed!", error);
        }).then(this.summarizeWorkDone);

the first is the factory method, very simple.

   doExitFactory(curTime,fn,stateScope) {
        console.log("doExitFactory " );//+ curTime,fn,stateScope );
        return this.doExit(curTime,fn,stateScope);
    }

this is the actual function to do the work. Calling a function dynamically on the given scope.

    doExit(curTime, _fn, stateScope) {
        console.log("doExit");
        return new Promise(function (resolve, reject) {   
          //  console.log("doing actual Exit stuff at time ");//+ curTime);
            _fn.apply(stateScope, [{sig: STATE.EXIT}, resolve, reject]);
            resolve();

        })
    }

The output looks like

TransitionCursor.setRoute
starting to create chain
new Exit Promise Chain...
new Exit Promise Chain...
finishing create chain


calling chain to start............
Transition *START*--------------------------
doExitFactory 
doExit
IS_s11 (EXIT)
doExitFactory 
doExit
IS_s1 (EXIT)
summarizeWorkDone:--------------- 

where IS_s11, and IS_s1 are the actual async functions I want to run in order.

TroyWorks
  • 411
  • 8
  • 16
-1

You wrote:

new Promise((r)=>r()).then(new Promise((r)=>r());

What you want to write:

new Promise((r)=>r()).then(()=>new Promise((r)=>r()));

To make your code works the way you want, change

lastPr.then(pr);

to

lastPr.then(()=>pr);
fantaghirocco
  • 4,761
  • 6
  • 38
  • 48
Hin Fan Chan
  • 1,543
  • 10
  • 11