0

I use nodejs on a Rasbperry pi to control its hardware pins. I was completely assuming that code such as:

for (..) {
  executeAsyncCode(..)
}

function executeAsyncCode() {
 doAsync1().then(doAsync2()).then(doAsync3())...
}

will eventually be executed in such manner that each execution of executeAsyncCode will be completely separated from others, meaning that two asynchronous executions wont be running at the same time. But real-time verification and usage shows differently. I encounter executions where doAsync1(..) is called one after the other for two executions of executeAsyncCode function, and doing a lot of mess during that.

To my usage its obviously a problem, as hardware cant be used in parallel, and there are many cases where I might want to execute code and rely on the fact that no locks are required. How can such code be limited to not execute all together? Is there a way of knowing how the event loop will execute code?

buddy123
  • 5,679
  • 10
  • 47
  • 73
  • Related: [Asynchronous for cycle in JavaScript](http://stackoverflow.com/questions/4288759/asynchronous-for-cycle-in-javascript) If you want to run one iteration at a time, you'll need an iterator that is aware of asynchronous operations. – Jonathan Lonowski Apr 19 '16 at 05:03
  • FYI, this `doAsync1().then(doAsync2()).then(doAsync3())...` should be this: `doAsync1().then(doAsync2).then(doAsync3);`. – jfriend00 Apr 19 '16 at 05:27

2 Answers2

1

All code will finish executing before the event loop will start the next context. That means that a loop will always execute to completion before any async callbacks are executed. For example:

for ( var i = 0; i < 100000; i++ ) {
    console.log( 'Hi!' );
    setTimeout( function ( ) { console.log( 'foo' ); }, 0 );
}

// Some synchronous operation that takes a long time 

console.log( 'bar' );

Will deterministically output 'Hi!' 100000 times followed by 'bar' and then since there is nothing more in the script the event loop gets a chance for the other messages to run and finally 'foo' is output 100000 times.

If you want to wait for the first promise chain to finish before starting the next one, you should return a Promise from executeAsyncCode so that you can start the next one when it completes:

var previousPromise = Promise.resolve();
for (..) {
  previousPromise = previousPromise.then( executeAsyncCode );
}

function executeAsyncCode() {
  return doAsync1().then(doAsync2).then(doAsync3)...
}
Paul
  • 139,544
  • 27
  • 275
  • 264
0

To wait for all your async code to finish before running them again you have to execute the next round inside the .then(). Since you are already using promises you only need to return the chain:

function executeAsyncCode() {
  return doAsync1().then(doAsync2).then(doAsync3)...
}

Then executeAsyncCode() can itself continue on a .then():

executeAsyncCode()
.then(function(){
    console.log('finished');
});

So, two loops would be:

executeAsyncCode()
.then(executeAsyncCode);

Five loops would be:

executeAsyncCode()
.then(executeAsyncCode)
.then(executeAsyncCode)
.then(executeAsyncCode)
.then(executeAsyncCode)
.then(executeAsyncCode);

To do an infinite loop, you can use the old setTimeout-style loop trick used by javascript animators and game programmers. This is an infinite loop:

function executeAsyncCode() {
  doAsync1()
  .then(doAsync2)
  .then(doAsync3)
  .then(executeAsyncCode);
}

Alternatively:

function executeAsyncCode() {
  doAsync1()
  .then(doAsync2)
  .then(doAsync3)
  .then(function(){
    executeAsyncCode();
  });
}

This second syntax allows us to write conditional loops:

function executeAsyncCode() {
  doAsync1()
  .then(doAsync2)
  .then(doAsync3)
  .then(function(){
    if (we_should_loop()) {
      executeAsyncCode();
    }
  });
}

We can also structure our code so that we can pass the loop condition as an argument. This is a simple countdown loop:

function executeAsyncCode(count) {
  doAsync1()
  .then(doAsync2)
  .then(doAsync3)
  .then(function(){
    count--;
    if (count) {
      executeAsyncCode(count);
    }
  });
}

So we can call it like this:

executeAsyncCode(5); // runs 5 loops

This emulates a simple for loop:

function executeAsyncCode(start, end) {
  doAsync1()
  .then(doAsync2)
  .then(doAsync3)
  .then(function(){
    start--;
    if (start < end) {
      executeAsyncCode(start,end);
    }
  });
}

executeAsyncCode(0, 20); // loops 20 times

This tries to emulate most of the features of a for loop:

function executeAsyncCode(x, checkfunction, incrementfunction) {
  if (checkfunction(x)) {
    doAsync1()
    .then(doAsync2)
    .then(doAsync3)
    .then(function(){
      executeAsyncCode(incrementfunction(x));
    });
  }
}

So you can sort of call it like a for loop:

// loops 10 times like a for loop:
executeAsyncCode(0, function(i){return i<10}, function(i){return i++});
slebetman
  • 109,858
  • 19
  • 140
  • 171
  • Note that while the code above looks like recursion they're not recursion. What they do instead is schedule tasks to execute at a later time. So there is no risk of the stack growing infinitely for the infinite loops. – slebetman Apr 19 '16 at 05:40