为什么 JS 模态消息框会在 setTimeout() 上暂停倒计时?
当 alert
之类的模态对话框窗口打开时,我遇到了 JS setTimeout
的意外行为,我想知道它背后的原因.
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.
我希望 setTimeout(fn,10000) 的意思是定期检查当前时间,当它大于 Now + 10000ms 时触发将调用传递的‘fn’函数的事件处理程序".这将是合乎逻辑的,看看我们如何将超时度量作为从现在开始的毫秒"传递.但是,显然,setTimeout
上的倒计时是文字倒计时,并且会在模式窗口打开时暂停.
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);
我希望警报 A 在您关闭警报 B 后立即显示(假设您等待了 15 秒),因为警报 A 超时仅 10 秒并且它们已经过去了.然而,实践表明,警报 A 的倒计时只是在警报 B 打开时暂停,并且仅在大约 10 分钟后才会显示.关闭警报 B 后又过了 9 秒,无论 B 打开了多长时间.
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.
这似乎不合逻辑.
更新.我绝对不是唯一一个在这里感到困惑的人,因为这种暂停超时的行为发生在 Chrome 和 Internet Explorer 中,但不是 Firefox.Firefox 执行我预期的行为 - 如果您在警报 B 上等待 15 秒 - 警报 A 会在您关闭它时立即弹出.
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.
推荐答案
我怀疑为什么 IE 和 Chrome 都会暂停挂起的计时器,直到 alert
被解除,并且火狐没有.我相信这只是因为在解释 W3C 的规范方面有一定的自由度代码>警报:
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
:
alert(message) 方法在调用时必须执行以下步骤:
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.
释放存储互斥锁.
向用户显示给定的消息.
Show the given message to the user.
(可选)在等待用户确认时暂停消息.
Optionally, pause while waiting for for the user to acknowledge the message.
第 4 步(暂停)进一步解释 这里:
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.
我可以肯定的是,除了调试目的之外,您不应该将 JavaScript 警报用于其他任何目的.警报确实允许暂停脚本执行(而 XHR 等一些异步操作正在后台进行),但它们对用户非常不友好.正确的方法是采用异步代码,使用 Promise,可能还有 ES6 generators/yeild
(如果你喜欢线性代码风格).
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).
以下问题高度相关,此处讨论了 alert
的一些替代方案:
The following question is highly related and some alternatives to alert
are discussed there:
- 防止 js alert() 暂停计时器
相关文章