While loops using Await Async.
Asked Answered
T

7

69

This Javascript function seems to use the while loop in an asynchronous way. Is it the correct way to use while loops with asynchronous conditions?

 var Boo;
 var Foo = await getBar(i)
 while(Foo) {
    Boo = await getBar3(i)
    if (Boo) {
      // something
    }
    Foo = await getBar(i)
    i++
  }

What I think it does is this:

var Boo;
var Foo;
getBar(i).then( (a) => {
  Foo = a;
  if(Foo) {
    getBar3(i).then( (a) => {
      Boo = a
      if(Boo) {
        //something
        i++;
        getBar(i).then( (a} => { Repeat itself...} 
      }
   }
  }
})

If that's totally false could you show another way to do it with async await + while loop?

Thanks!!

Twain answered 23/8, 2016 at 21:13 Comment(4)
Just FYI, async/await is not a part of ES 6.Wardwarde
await transforms into state machines. You can either have a number of small state machines as you've written (very hard to reason about), or you can have one larger state machine (which is what e.g. C# does).Bine
What do you mean by "the correct way"? If this code does what you want, then it's correct.Wardwarde
I could not test it as much as I want and it works as expected in my interpretation of what it should do but not sure if it is not just luck.Twain
B
44

Is it the correct way to use while loops with asynchronous conditions?

Yes. async functions simply suspend their execution on every await until the respective promises fulfills, and any control structures continue to work as before.

Bethanie answered 23/8, 2016 at 21:18 Comment(0)
M
39

Yep, it's fine to do it like this:

let stopped = false
        
// infinite loop
while(!stopped) {
    let res = await fetch('api link') 
    if (res.something) stopped = true // stop when you want
}

But be careful not to get infinite loop

Mv answered 2/11, 2019 at 20:9 Comment(3)
Be sure to handle the case where something is always false, otherwise your loop will stay infinite.Upraise
await can only be used in an async functionClintonclintonia
@DavidWelborn top level await is also supported if you configure ts as moduleMv
S
13

Is it the correct way to use while loops with asynchronous conditions?

Yes, provided that getBar and getBar3 are asynchronous functions (marked as async or just returning a Promise).

Of course the execution should be inside an asynchronous context (inside async function)

A possible issue that I can see is that initially there are 2 executions of getBar with the same i and the rest of executions use a mismatched i between while and if. If this is not the desired behavior perhaps a more correct version would be:

    (async ()=>{
     while(await getBar(i)) {    
        if (await getBar3(i)) {
          //do something
        }
        i++;
      }
    })();

See a mocked example here

Scopas answered 4/3, 2020 at 13:15 Comment(0)
M
3

A simple way would be:

// returns a delayed promise 
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))

// performs a specific (mathematical) operation every "ms" (milliseconds)
const dec = async (i, ms) => (await delay(ms), --i)

const foo = async () => {
  let i = 5;
  
  // runs an async operation which decreases "i" by 1 every 500ms
  while(i = await dec(i, 500)) 
    // prints 4, 3, 2, 1 with a delay 
    console.log(i) 
}

foo()

Here's my GIST if you need to star it to save for later.

Alternatively, you can count up:

// returns a delayed promise 
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))

// performs a specific (mathematical) operation every "ms" (miliseconds)
const inc = async (i, ms) => (await delay(ms), ++i)

const foo = async () => {
  let i = 0;
  
  // runs an async operation which increases "i" by 1 every 500ms
  while((i = await inc(i, 500)) < 5) {
    // prints 1,2,3,4 with a delay 
    console.log(i) 
  }
}

foo()
Meatman answered 19/3, 2022 at 14:8 Comment(0)
D
3

Based on this example, we see that using await in the while loop triggers SYNCHRONOUS code execution. In other words, until one iteration of the loop is completed, the SECOND one WILL NOT BE STARTED

let counter = 0;

const promise = () =>
  new Promise((resolve, reject) =>
    setTimeout(() => {
      resolve(1);
    }, 2000)
  );

(async() => {
  console.log("Start")
  console.time('allTime');
  while (counter < 3) {
    await promise();
    console.log('Resolve promise', counter);
    counter++;
  }
  console.timeEnd('allTime');
})();
Dinadinah answered 6/2, 2023 at 22:37 Comment(0)
C
3

Since while loop body works synchronously, we need to move our code out of the body.
This solution might help someone:

while(await (async () => {
  Boo = await getBar3(i)
  if (Boo) {
    // something
  }
  Foo = await getBar(i)
  i++
  return Foo; // you must return the condition that the loop depends on it
})()); // Adding this semicolon is crucial
Controvert answered 6/4, 2023 at 14:45 Comment(0)
C
-1

Its been awhile since this question was asked. I am new to js after many years of other languages (starting with punch cards and paper tape) and needed to solve this problem. Here is my answer:

var loopContinue = true;
var n = 0;

async function  Managework() {
  while (loopContinue) {  //seemingly an infinite loop
    //await (doWork(n));
    await (doWork(n).catch(() => { loopContinue=false; }));
    n++;
    console.log(`loop counter ${n}`);
  }
  console.log(`loop exit n=${n} loopContinue=${loopContinue}`);
 }

Managework();

function doWork(n) {
 return new Promise((resolve, reject) => {
    console.log(`dowork(${n})`);
    if (n > 5) {
      //loopContinue = false;
      reject(`done`);
    }
    setTimeout(() => {
      resolve('ok');
    }, 1000);
  });
}

As desired the loop breaks after the 5th iteration. 'loopContinue' global can either be set in the work function or in the catch (or could be the then) of the promise. I tired just using 'break' in the then or catch but I get an error.

If you want to do it in doWork you can eliminate the catch and and just call doWork() and uncomment the // loopContinue= false in doWork. Either way works. This was tested with node.js

I found stuff on nextTick but this seems much easier.

Coir answered 8/2, 2018 at 23:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.