I have encountered an unexpected behavior of JS setTimeout
when modal dialog windows like alert
are open and I would like to know the reason behind it.
I expected setTimeout(fn,10000) to mean "check current time periodically and when it is greater than Now + 10000ms fire the event handler that will invoke the passed 'fn' function". This would be logical, seeing how we pass the timeout measure as 'ms from now'. But, apparently, the countdown on setTimeout
is a literal countdown and will be paused while a modal window is open.
setTimeout(function(){
//alert A
alert("10 seconds have passed for the first setTimeout")
}, 10000);
setTimeout(function(){
//alert B
alert("Wait for 15 seconds and press OK");
},1000);
I would expect alert A to display immediately after you close alert B (presuming you waited for 15 sec. to do so), since alert A timeout was just for 10 sec and they have already passed. Practice, however, shows that countdown to alert A is simply paused while alert B is open and it will show only after approx. 9 more seconds have passed after you've closed alert B, no matter how long B was open.
This does not seem logical.
Update. I'm definitely not the only one confused here, because this behavior of pausing the timeout occurs in Chrome and Internet Explorer, but not Firefox. Firefox executes the behavior I expected - if you wait for 15 seconds on alert B - alert A pops out instantly whenever you close it.
setTimeout() is an asynchronous function, meaning that the timer function will not pause execution of other functions in the functions stack. In other words, you cannot use setTimeout() to create a "pause" before the next function in the function stack fires.
1) schedules toggleModal to run in 5 seconds' time 2) toggles the show-modal class. 1) is the important thing here: the function schedules itself to run again in 5 seconds, each time it runs - thereby ensuring that, when called once, it will continue to be called every 5 seconds from then onwards. Save this answer.
The setTimeout() is executed only once. If you need repeated executions, use setInterval() instead. Use the clearTimeout() method to prevent the function from starting.
Next, you can pass the milliseconds parameter, which will be the amount of time JavaScript will wait before executing the code. If you omit the second parameter, then setTimeout() will immediately execute the passed function without waiting at all.
I doubt there is a definitive answer on why both IE and Chrome put a pause on pending timers until alert
has been dismissed, and Firefox doesn't. I believe it's just because there is certain freedom in interpreting the W3C's specs for alert
:
The alert(message) method, when invoked, must run the following steps:
If the event loop's termination nesting level is non-zero, optionally abort these steps.
Release the storage mutex.
Show the given message to the user.
Optionally, pause while waiting for for the user to acknowledge the message.
Step 4 (the pause) is further explained here:
Some of the algorithms in this specification, for historical reasons, require the user agent to pause while running a task until a condition goal is met. This means running the following steps:
If any asynchronously-running algorithms are awaiting a stable state, then run their synchronous section and then resume running their asynchronous algorithm. (See the event loop processing model definition above for details.)
If necessary, update the rendering or user interface of any Document or browsing context to reflect the current state.
Wait until the condition goal is met. While a user agent has a paused task, the corresponding event loop must not run further tasks, and any script in the currently running task must block. User agents should remain responsive to user input while paused, however, albeit in a reduced capacity since the event loop will not be doing anything.
So, the event loop gets suspended in any case. The callback of the longer timeout doesn't get invoked while the alert is still visible and modal. Had it not been this way, all kinds of nasties might be made possible, like multiple alerts on top of each other.
Now, can you tell from the above specs if the timer countdown should be suspended for the lifetime of the alert, or should rather be fired as soon as the alert has gone? I can't, and I'm not even sure which behavior would be more logical.
What I'm sure about is that you shouldn't be using JavaScript alerts for anything else but debugging purposes. Alerts do allow to suspend script execution (while some asynchronous operation like XHR is taking place in the background), but they're quite unfriendly to the user. The right approach would be embrace asynchronous code, using promises and, possibly, ES6 generators/yeild
(if you're after the linear code style).
The following question is highly related and some alternatives to alert
are discussed there:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With