0

Very often I end up writing loops with awaits inside of them, in order to perform some tasks sequentially or achieve a certain interval between iterations.

For example:

for (const item of items) {
  await doSomthing(item);
}

or:

while(true) {
  await doSomeTask();
  await delay(60000);  
}

However, ESLint is reprimanding me for writing this kind of code.

What is an alternative pattern to sequential looping with awaiting and infinite loops with awaits inside, when I don't want to run all async tasks at the same time and rather want to run them at a slow pace?

2 Answers2

3

ESLint is reprimanding me for writing this kind of code.

The solution here is to disable ESLint - either individually for those lines, or that horrible rule in general. Even the rule docs themselves say "In many cases […] it makes sense to use await within a loop and it is recommended to disable the rule via a standard ESLint disable comment".

The code you are writing is perfectly appropriate for performing tasks sequentially. If you want to avoid it, there's only recursion as an alternative that the linter can't detect :-)

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I wouldn't call it a "horrible rule". It is simply pointing out some code that does not take full advantage of the async/await capabilities. You are correct that the above code is perfectly valid when certain statements should be executed sequentially, however, if there is no necessity, then you should use Promise.all() to get an efficiency/speed boost. – Tim Klein Aug 27 '18 at 17:38
  • @TimKlein I think it's horrible to make it an error by default. Also for me the rate of false positives is far too high, but maybe that's just me who already knows when to use which. A linter is not useful when you have the feeling that it "reprimands" you instead of offering helpful advise. – Bergi Aug 27 '18 at 17:44
  • @TimKlein Running things in series is certainly not always a bad thing,. The biggest problem I have with `Promise.all` is it has no built in throttling, Bluebirds `map` with the concurrency option should really be what `Promise.all` became. – Keith Aug 27 '18 at 17:56
1

You could use a recursive function to achieve the same effect, and this allows you to more fully leverage the asynchronous nature of what you're doing. Your main thread can continue on and do other things rather than waiting for the end of a for loop - any code that relies on that section being complete can be executed in the callback function that you pass in.

function asynchronousFunction(thing){
  //do something
  console.log('doing something')
  return new Promise(resolve => {
    setTimeout(e => resolve(), 1000)
  })
}

async function doSomething(arrayOfThings, next){
  const thing = arrayOfThings.pop()
  const responseFromAsyncFunction = await asynchronousFunction(thing)
  console.log('after', arrayOfThings.length)
  if(arrayOfThings.length) doSomething(arrayOfThings, next)
  else next()
}

const myData = [1,2,3,4,5]

doSomething(myData.slice(0), function(){ console.log('done') })
jmcgriz
  • 2,819
  • 1
  • 9
  • 11