7

I have nodejs app that needs a few infinite loops which call async functions. I was considering implementing the following:

async function execute1() {
   ...do some async work...
}

async function execute2() {
   ...do some async work...
}

setInterval(execute1, 500)
setInterval(execute2, 500)

My concern is that if the async functions will take a long time to complete, the open references will pile up and this can result in a memory crash down the line.

  1. is setInterval the right tool for this job? is there a more suitable tool?
  2. What is the most elegant method to make sure the execute() function will not start if the previous run hasn't return?
isherwood
  • 58,414
  • 16
  • 114
  • 157
amit
  • 2,171
  • 4
  • 31
  • 50

4 Answers4

12

setInterval isn't the right tool because it's unaware of promises and can't maintain correct control flow.

It can be async function with infinite loop:

async function execute1() {
  while (true) {
    await new Promise(resolve => setTimeout(resolve, 500));
    // ...do some async work...  
  }
}

execute1();
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • How to `break` out of the `while` loop? – guest271314 Feb 11 '19 at 17:05
  • @guest271314 By using a condition instead of `true`. – Estus Flask Feb 11 '19 at 17:07
  • FWIW One pattern that used for a condition to achieve breaking the loop `let done; while ((done = await new Promise(resolve => ..}).then(() => return !done})))` [Why does assignment using await within while expression not retain assigned value?](https://stackoverflow.com/a/47172222/). And a verbose approach based on the former pattern [Run multiple recursive Promises and break when requested](https://stackoverflow.com/a/48349837/2801559) – guest271314 Feb 11 '19 at 17:12
4

setTimeout might work better in this case.

async function execute1(delay) {
   // await ...
   setTimeout(() => execute1(delay), delay)
}
execute1(500)
1

you can use simple flags to indicate whether the previous function is still running

let isRunning = false;

async function execute1() {
   if (isRunning) return;
   isRunning = true

   ...do some async work...

   // make sure to call this whether it succeeds or fails, maybe in a finally block
   isRunning = false
}

setInterval(execute1, 500)
gafi
  • 12,113
  • 2
  • 30
  • 32
  • Fair, but I think the setTimeout it likely the easier solution for this one. – Bibberty Feb 11 '19 at 16:57
  • May not be obvious to some but if `...do some async work...` does not contain any `await` statements, the `if (isRunning)` will never execute when `isRunning === true`. – Patrick Roberts Feb 11 '19 at 18:27
0

You can create a function that will run your execute1(), then wait for it's completion, then run itself with setTimeout().

function randomSleep() {
  return new Promise(resolve => setTimeout(resolve, Math.random() * 3000));
}

let done = 0;

async function execute1() {
   console.log('Execute 1 started.')
   await randomSleep()
   return 'done'
}

const run = () => {
  execute1().then(result => {
    console.log('Execute 1 ended')
    done++
    console.log(`Done ${done} times.`)
    console.log(`Now waiting for 2 seconds`)
    setTimeout(() => {
      run()
    }, 2000)
  })
}

run()
dporechny
  • 638
  • 5
  • 12
  • Tested this in node.js. Shouldn't it work withour the randomSleep part? When removing that part this does not wait for the execute1() to finish. – Nikolas Feb 20 '20 at 15:48