2

How do I make the below loop wait for current iteration to finish before it starts the next iteration?

var minute = 0

alert ("Kick Off");

var refreshIntervalId = setInterval(function() {     
  if (minute < 91) {
    // lots of code with Ajax calls
    // can take from a fraction of a second to 30 seconds to run code
    // depending on conditions
  } else {
    clearInterval(refreshIntervalId);
  } 
  minute++         
}, 1000);

I usually use functions and '.done' to execute code after pre-requisite code has finished. I have also used functions within animations to execute code after the animation has finished.

However I can't get my head around how you make the code to wait for an iteration of a loop to finish. If I put the pre-requisite code into a function (all the code within the loop) and then use .done, there is only the closing brackets of the loop left to put within a '.done' function - which obviously will not work.

Can anyone solve this conundrum?

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
BenSellars
  • 79
  • 9
  • use async await – Bhumit 070 Jan 30 '21 at 19:02
  • So, your issue is that your interval is 1 second, but some activity within that function might take longer than 1 second. Correct? In which case you want to delay the triggering of the next interval until the last one is finished? But you still want 90 iterations, even if one of those iterations takes longer than 1 second? – Neil W Jan 30 '21 at 19:08
  • start by never using `alert` every again, and using `console.log` if you need to "see things happening". With that said, if you have code that takes 30 seconds to run, split that up into discrete steps, and then make each step an `async` function, and get their function results using `await` so that the JS scheduler can interrupt execution every time it sees an `await` and keep things responsive. Also, *never* use `setInterval` for unknown-runtime code like this. Make the very end of your code path retrigger its code path, instead, so that you run "the next iteration" only when you're done. – Mike 'Pomax' Kamermans Jan 30 '21 at 19:12
  • Neil W, yes absolutely. Most iterations I need to last 1 second, but the longer ones are the important ones and need to either be set to a longer time or wait until the code has finished. . – BenSellars Jan 30 '21 at 19:12
  • I tried using a variable for the time, but didn't work. – BenSellars Jan 30 '21 at 19:13
  • Thanks Mike. I'll start working with the console log more. – BenSellars Jan 30 '21 at 19:16
  • Also seems sensible, that if the code waits for each step then it should wait long enough for the block of code to finish. Never used await before, so will look it up. – BenSellars Jan 30 '21 at 19:18

2 Answers2

3

You can make a while loop in an async function instead and use await. In the code below, the while-loop waits at least 2 seconds at each iteration but at most as long as the task takes.

const sleep = time => new Promise(resolve => setTimeout(resolve, time))  

async function main() {
  while (true) {
    console.log("new iteration")
    const task = new Promise((resolve, reject) => {
      // things that take long go here…
      const duration = Math.random()*4000
      setTimeout(() => {
        console.log(`done, task took ${Math.round(duration)}ms`)
        resolve()
      }, duration)
    })
    // wait until task is finished but at least 2 seconds
    await Promise.all([task, sleep(2000)])
  }
}

main()
Matthias
  • 737
  • 8
  • 14
1

To bring Matthias's elegant up-voted answer to life in your specific situation:

var minute = 0
// reusable function to wait the specified amount of time (1000ms in your case)
const sleep = time => new Promise(resolve => setTimeout(resolve, time))  

alert ("Kick Off");

while (minute < 91)
{
    const processThisMinuteTask = new Promise((res, rej) => {
        // lots of code with Ajax calls
        // can take from a fraction of a second to 30 seconds to run code
        // depending on conditions
        // NOTE: use AWAIT for all Ajax calls
        res();
    });
    // will wait for both:
    // a) your processThisMinuteTask to finsih
    // b) 1000ms to pass
    // So, will mean that this iteration will wait for whichever of those is longest
    await Promise.all([processThisMinuteTask, sleep(1000)]);
    minute++;
}

P.S. Nothing wrong with the "Alert" if what you want to do is prompt the user that kick-off is about to start, but will not start until they acknowledge the alert.

Further to comment about "await is a reserved identifier", the enclosing function needs to be declared as async.

function playMatch() {
   // will fail
   await myOtherFunction();

   // more code
}

Needs to be:

async function playMatch() {
    // will now wait for myOtherFunction to finish before continuing.
    await myOtherFunction();

    // more code
}
Neil W
  • 7,670
  • 3
  • 28
  • 41
  • Thank you both. It looks like a brilliant solution. Waiting for whichever is largest is perfect. I've tried and get 'Uncaught SyntaxError: await is a reserved identifier'. Though I have not yet used await for any Ajax calls, so may be this. I need to read up more on how await works.The "Alert" is just a placeholder that will be replaced by a pretty pop-up eventually. Though I may have messed up by not mentioning JQuery animation lengths within the loop. AJAX call lengths.seem to take >1sec, but its the JQuery animations take up to 30 seconds. – BenSellars Jan 30 '21 at 20:33
  • 1
    await needs to be enclosed in an function declared with async. see updated answer. Also, do read up on async / await. But also read up on Promises. async / await is a more elegant syntax for handling Promises, so a good working knowledge of Promises will be helpful. – Neil W Jan 30 '21 at 20:44
  • You've been a massive help. I've read up and logically it all makes sense. However. If I add 'async function play_match(){' above var minute = 0 and close brackets after end of while loop it just waits forever. Doesn't even alert "kick off". – BenSellars Jan 31 '21 at 12:51
  • I'm thinking either the '.done' Ajax calls all need re-writing using 'async' and 'await' instead (as they may clash and just wait for each other) or I need to just work on using await with some smaller, simpler pieces of code, until I've mastered its basic use. – BenSellars Jan 31 '21 at 12:52
  • "await is a reserved identifier" error has been resolved though. – BenSellars Jan 31 '21 at 12:55
  • The following thread may be relevant to your new challenge ... https://stackoverflow.com/questions/29808915/why-use-async-await-all-the-way-down Do you 'await' the call to "play_match"? – Neil W Jan 31 '21 at 13:14
  • No I tried adding an 'minute_iteration ()' function within the while loop, before minute++ and 'await minute_iteration' after minute++ before closing the async function 'play_match()'. – BenSellars Jan 31 '21 at 18:59
  • More reading required I think, and testing the syntax on smaller, simpler blocks of code. Thanks for the link. – BenSellars Jan 31 '21 at 19:00
  • I forgot to call the function :0D – BenSellars Feb 02 '21 at 09:35