0

I Want to make a loop to live 2 seconds, then brake after. This code wont work, the loop goes to end, it wont brake at 2 seconds. Why?

var stop = 0;
setTimeout(function() {
  stop = 1;
}, 2000);

for (var i = 0; i < 10000; i++) {
  console.log("wait" + stop);
  if (stop == 1) break;
}

Is there a workarround?

superbem
  • 441
  • 3
  • 10
  • 4
    JavaScript is not preemptable. The timeout function doesn't run until the script returns to the main event loop. – Barmar Jan 28 '20 at 22:40
  • This sort of feels like a dupe of [What is the JavaScript version of sleep()?](https://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep) but also I get heavy impression of [an XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Can you explain *why* you want to do this? What are you trying to solve by "stopping" after 2 seconds? – VLAZ Jan 28 '20 at 22:44
  • Also, your for loop would most likely complete 10000 iterations before 2 seconds is up anyway. Do you want a loop that runs for exactly 2 seconds? Or do you want a loop that runs a number of times and exits either when it's complete or after 2 seconds (whichever happens first)? – Brett Gregson Jan 28 '20 at 22:45
  • Add `, timeout = Date.now()+2000; Date.now() < timeout &&` to your loop head – Bergi Jan 28 '20 at 22:45
  • 1
    You can busy wait by checking the time repeatedly, e.g. with `Date`, `performance.now` or similar. Apart from really weird scenarios, exploits and related, there is no sense in doing so though. – ASDFGerte Jan 28 '20 at 22:46
  • 1
    Technically you could use a web worker to run in the background and message you once the two seconds have passed. Multithreading can only be achieved with workers, however, afaik, you're limited to one foreground and one background thread. – Psi Jan 28 '20 at 22:50
  • 2
    Why do you want to block everything for 2 seconds? – Barmar Jan 28 '20 at 22:55

1 Answers1

2

A setTimeout callback will never interrupt a running synchronous loop. To achieve the desired result, you can change your loop head to

for (var i=0, timeout=Date.now()+2000; i<10000 && Date.now()<timeout; i++) …
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • The `i` is likely not even needed, either. If a loop needs to run for 2 seconds, then just the Date part of the condition can stay. But I'm not really sure what OP wants this for, I suspect there is a better solution for the underlying problem. – VLAZ Jan 28 '20 at 22:49
  • @VLAZ I expected the OP actually did something else in that loop other than waiting. But if not, yeah you're right, using a loop for busy-waiting is really bad idea. – Bergi Jan 28 '20 at 22:51
  • In order to match the original code, this is the correct way to state the answer, including the `i`. – Psi Jan 28 '20 at 22:51
  • That's near what I want, but I need the code to be fast, and I think a get date.now for every iteration slower than a var. – superbem Jan 28 '20 at 23:21
  • 1
    @superbem You can also do `(i % 100 || Date.now() < timeout)` to only calculate the current date only every hundred iterations or something. But really, if did worry about making the code fast, you wouldn't have a synchronous 2-second loop in it in the first place, and if you expect a mere 10000 iterations to take longer than 2 seconds then probably the loop body is heavy enough that a single `Date.now()` call pales in comparison. – Bergi Jan 28 '20 at 23:52
  • @Bergi The % computation is slow too comparing to a bit var. I have much more iterations than 10000 on what I want to implement. – superbem Jan 29 '20 at 01:28
  • @superbem What do you mean by "*a bit var*"? But surely if you know how the various operators compare in speed, you can devise (and benchmark) a solution that is fast enough for your purposes and checks the time only every few iterations. – Bergi Jan 29 '20 at 01:34