简体   繁体   中英

Break out of while loop with Async/Await

Context : I have a while loop that i want to run 10 times and within i have async/awaait code that runs every 3 seconds. The while loop works as sort of a timeout if it ever runs 10 times and the async/await check doesnt return the expected value then, break out of the while loop the process timed out.

Problem : The break out of loop portion of the code is running first with the value of i(loop variable) maxed out. As i figure the way i have it setup i have no access to the value of i while is looping and only when i is at its max value.

Question : How can i escape out of this loop early when condition is met or if i is exhausted?

var i = 0;

    //Run 10 Times
    while (i < 10) {

        //Run every 3 seconds
        ((i) => {
            setTimeout( async () => {
                isAuthenticated = await eel.is_authenticated()();
                sessionStorage.sessionStatus = JSON.stringify(isAuthenticated);
                console.log('------NEW STATUS-------');
                console.log(JSON.parse(sessionStorage.sessionStatus).authenticated);
                console.log('Inside:' + i);
            }, 3000 * i)
        })(i++);

        //Break out of loop early if condition is met or I is exhausted, but it only runs 1 time and i is always max
        if (i === 9 || JSON.parse(sessionStorage.sessionStatus).authenticated) {
            console.log('Outside:' + i);
            checkStatus('retried');
            break;
        }
    }

NOTE: In case anyone is wondering eel.is_authenticated()(); is not a typo, its a python library to create desktop applications the double ()() is normal.

Also if this approach is overly complicated for what it does, any other ways to approach it are welcome:)

Thanks

The issue here is that you are running through all your loop iterations immediately (10 times), setting up 10 timeouts in the process, 3s apart from each other:

  1. Your loop runs 10 times, creating 10 timeouts
  2. You reach the i === 9 case
  3. 3s later, 1st timeout runs
  4. 3s later, 2nd timeout runs
  5. ...

What you want is for the loop to actually wait 3s between iterations. For that, you'd use setTimeout in a different way - you'd create a promise that resolves when the timeout is hit and await that promise:

// As helper function for readability
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

// Your loop
let i;
for (i = 0; i < 10; i++) {
  // Wait 3s
  await delay(3000);

  // Check authentication
  const isAuthenticated = await eel.is_authenticated()();
  sessionStorage.sessionStatus = JSON.stringify(isAuthenticated);
  console.log('------NEW STATUS-------');
  console.log(JSON.parse(sessionStorage.sessionStatus).authenticated);
  console.log('Inside:' + i);

  // Break loop if authenticated
  if (isAuthenticated.authenticated) break;
}

// We were authenticated, or looped 10 times
// Note: Because I moved this outside, i will now actually be 10 if the loop
// ended by itself, not 9.
console.log('Outside:' + i);
checkStatus('retried');

One consequence here though would be that if the call to is_authenticated takes a significant amount of time, the checks will be more than 3s apart, because we are now waiting for 3s and for this call. If this is undesired, we can reduce the delay time based on how much time elapsed since the last call:

// As helper function for readability
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

// We will save here when the last delay completed, so that the checks are always
// 3s apart (unless the check takes longer than 3s)
// Initially we save the current time so that the first wait is always 3s, as before
let lastIteration = Date.now();

// Your loop
let i;
for (i = 0; i < 10; i++) {
  // Wait until 3s after last iteration (limited to 0ms, negative waits won't work)
  await delay(Math.max(lastIteration + 3000 - Date.now(), 0));

  // Update "last iteration" time so the next delay will wait until 3s from now again
  lastIteration = Date.now();

  // Check authentication
  const isAuthenticated = await eel.is_authenticated()();
  sessionStorage.sessionStatus = JSON.stringify(isAuthenticated);
  console.log('------NEW STATUS-------');
  console.log(JSON.parse(sessionStorage.sessionStatus).authenticated);
  console.log('Inside:' + i);

  // Break loop if authenticated
  if (isAuthenticated.authenticated) break;
}

// We were authenticated, or looped 10 times
// Note: Because I moved this outside, i will now actually be 10 if the loop
// ended by itself, not 9.
console.log('Outside:' + i);
checkStatus('retried');

All of this assumes that the function in which this code is located is async . If it isn't, you need to make it async but then you need to remember adding a .catch(e => handleTheErrorSomehow(e)) when you call it, to avoid unhandled promise rejections!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM