为什么JS模态消息框会在setTimeout()上暂停倒计时?

Iva*_*lev 5 javascript confirm alert modal-dialog settimeout

setTimeout当模态对话框窗口alert打开时,我遇到了JS的意外行为,我想知道它背后的原因.

我期望setTimeout(fn,10000)表示"定期检查当前时间,当它大于Now + 10000ms时,激活将调用传递的'fn'函数的事件处理程序".这是合乎逻辑的,看看我们如何将超时测量值传递为"ms from now".但是,显然,倒计时setTimeout是一个字面倒计时,并在模态窗口打开时暂停.

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);
Run Code Online (Sandbox Code Playgroud)

在关闭警报B(假设您等待15秒)之后,我希望警报A立即显示,因为警报A超时仅持续10秒并且它们已经通过.但是,练习表明,当警报B打开时,警报A的倒计时只是暂停,并且仅在大约后显示.无论B打开多长时间,在关闭警报B后还有9秒钟过去了.

这似乎不符合逻辑.

更新.我绝对不是唯一一个在这里混淆的人,因为这种暂停超时的行为发生在Chrome和Internet Explorer中,而不是Firefox.Firefox会执行我预期的行为 - 如果您在警报B上等待15秒 - 警报A会在您关闭它时立即弹出.

nos*_*tio 5

我怀疑有一个确定的答案,为什么IE和Chrome都会暂停待处理的计时器,直到alert被解雇,而Firefox则没有.我相信这只是因为在解释W3C规范方面alert有一定的自由度:

调用alert(message)方法时,必须运行以下步骤:

  1. 如果事件循环的终止嵌套级别非零,则可选择中止这些步骤.

  2. 释放存储互斥锁.

  3. 向用户显示给定的消息.

  4. (可选)在等待用户确认消息时暂停.

这里进一步解释步骤4(暂停):

出于历史原因,本规范中的一些算法要求用户代理在运行任务时暂停,直到满足条件目标.这意味着运行以下步骤:

  1. 如果任何异步运行的算法正在等待稳定状态,则运行其同步部分,然后继续运行其异步算法.(有关详细信息,请参阅上面的事件循环处理模型定义.)

  2. 如有必要,请更新任何文档或浏览上下文的呈现或用户界面以反映当前状态.

  3. 等到达到条件目标.当用户代理具有暂停的任务时,相应的事件循环不能运行更多任务,并且当前运行的任务中的任何脚本都必须阻止.用户代理应在暂停时保持对用户输入的响应,但是,尽管容量减小,因为事件循环不会执行任何操作.

因此,事件循环在任何情况下都会被暂停.当警报仍然可见且模态时,不会调用较长超时的回调.如果不是这样的话,各种恶意都可能成为可能,就像多个警报一样.

现在,您能从上述规范中了解是否应该在警报的生命周期内暂停计时器倒计时,或者应该在警报消失后立即触发?我不能,而且我甚至不确定哪种行为会更合乎逻辑.

我确定的是,除了调试之外,您不应该将JavaScript警报用于其他任何事情.警报允许暂停脚本执行(虽然某些异步操作如XHR正在后台进行),但它们对用户非常不友好.正确的方法是使用promises和可能的ES6生成器/yeild(如果你是在线性代码样式之后)接受异步代码.

以下问题高度相关,并alert在那里讨论了一些替代方案: