0

I have a function that should like

    function longFunc(par1,par2)
    {
        var retVal = [], stopReq = false;
        function evtLs() { stopReq = true; }
        something.on("event",  evtLs);
        for(var i=0;i<possibleResults1.length;i++) { 
            if(isValid(possibleResults1[i])) 
                retVal.push(possibleResults1[i]); 
        }
        if(stopReq) return retVal;
        if(canAddResult2()) {
            for(var i=0;i<possibleResults2.length;i++) { 
                if(isValid(possibleResults2[i])) 
                    retVal.push(possibleResults2[i]); 
            }
            if(stopReq) return retVal;
        } else {
            for(var i=0;i<possibleResults3.length;i++) { 
                if(isValid(possibleResults3[i])) 
                    retVal.push(possibleResults3[i]); 
            }
            if(stopReq) return retVal;
        }
        something.removeListener("event", evtLs);
        return retVal;
    }

My problem is that, obliviously the event is never emitted because the process is engaged in longFunc.

How can I convert it to allow event emitting?

Here a testable example:

    var cancelled = false;

    setTimeout(() => cancelled = true,1000);
    function main()
    {
       var ret = []
       var totalStart =Date.now(); 
       for(var i=0;i<20;i++)
       {
          var v
          var start =  Date.now();
          while((Date.now()-start)<100)
          {
             v=Math.sqrt(Math.random());
          }
          ret.push(v);
          if(cancelled) break;
       }
       console.log("delta:"+(Date.now()-totalStart));
       return ret;
    }

    var r = main()
    console.log(r.length)

I expect that stop after 1000, but instead stops after 2000.

Perry
  • 1,113
  • 2
  • 10
  • 22
  • What is the overall code supposed to do? – Jonas Wilms May 07 '19 at 08:50
  • if all those `do thinks and can retVal.push();` are synchronous, then `evtLs` can not be called until they all finish anyway - you probably know this, but without any clue as to what "thinks" you're "doing", the answer is a coconut – Jaromanda X May 07 '19 at 08:51
  • It is a web service, in fact longfunc is itself an event listener, but can happen that the client call another web method to stop the last request. – Perry May 07 '19 at 08:54
  • 1
    without knowing the details of what "thinks" you're "doing" in those loops, the answer is still a coconut – Jaromanda X May 07 '19 at 08:55
  • I don't think the code inside the "do thinks and can retVal.push();" is important here, anyway, it is like: `for(var i=0;i – Perry May 07 '19 at 08:56

2 Answers2

1

JavaScript has a principle, which is often called cooperative concurrency in other languages: Your code will run synchronously and without being interrupted unless it explicitly plans to be interrupted. In your case the code will run without interruption, any event triggered will be executed after that code finished. Now to support being interrupted you have to stop the execution sometimes in the middle, async functions are really useful for that:

  function interruptable(task) {
     let cancelled = false;
     const tick = () => new Promise(res => setTimeout(res));

     const done = task(() => cancelled ? Promise.reject() : tick()).catch(e => e);


      return { done, cancel() { cancelled = true } };
  }



  function taskWithInterrupt() {
     const result = [];

     const { cancel, done } = interruptable(async function(tick) { // we enter the async execution flow
        doTaskA();
        await tick(); // allow for interrupts here
        doTaskB();
        await tick();
      });

      someKillSwitch.on("event", cancel);

       return done.then(() => result);
   }
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
0

After some tests, and looking for help in another question Node.js: allow process update during loop I solved my problem using setImmediate, in this way:

var cancelled = false;

setTimeout(() => { console.log("cancel"); cancelled = true},1000);

main = new Promise((resolve,reject)=> {
   var ret = []
   function Step(i) {
      try {
         console.log("step "+i);
         var start =  Date.now();
         while((Date.now()-start)<100)
         {
            v=Math.sqrt(Math.random());
         }
         ret.push(v);
         if(i==20 || cancelled)
         {
            resolve(ret);
            return
         }
         setImmediate(Step.bind(null,i+1))            
      } catch (error) {
         reject(error)
      }
   }
   Step(0);
});

var totalStart =Date.now(); 
main.then((r)=>{
   console.log("delta:"+(Date.now()-totalStart));
   console.log(r.length)   
})

In this way the output is:

step 0
step 1
step 2
step 3
step 4
step 5
step 6
step 7
step 8
step 9
cancel
step 10
delta:1004
11

so it stops after 1000ms , as expected

Perry
  • 1,113
  • 2
  • 10
  • 22