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

时间:2014-10-21 13:01:38

标签: javascript modal-dialog settimeout alert confirm

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

我期望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);

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

这似乎不符合逻辑。

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

3 个答案:

答案 0 :(得分:5)

我怀疑对于为什么IE和Chrome都暂停待处理的计时器直到alert被解雇为止,并且Firefox没有明确的答案。我相信这只是因为在解释W3C's specs for alert时有一定的自由:

  

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

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

  2.   
  3. 释放存储互斥。

  4.   
  5. 向用户显示给定的消息。

  6.   
  7. 可选择在等待用户确认时暂停   消息。

  8.   

步骤4(暂停)将进一步解释here

  

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

     
      
  1. 如果有任何异步运行的算法正在等待稳定状态,   然后运行他们的同步部分,然后继续运行他们的   异步算法。 (参见event loop处理模型   以上定义详情。)

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

  4.   
  5. 等到达到条件目标。用户代理暂停时   任务,相应的事件循环不能运行任何进一步的任务   当前运行的任务中的脚本必须阻止。用户代理应该   但是,虽然暂停,但仍然会在暂停时保持对用户输入的响应   因为事件循环不会做任何事情而减少了容量。

  6.   

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

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

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

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

答案 1 :(得分:-1)

警告是UI阻止,因为Javascript是单线程的,它将阻止任何操作直到对话框被解除。

答案 2 :(得分:-1)

如果你真的需要,你可以使用自己的计时器。

var now = Date.now();
function alertA(){
    //alert A
    alert("10 seconds have passed for the first setTimeout")
}
setTimeout(function(){
    //alert B
    alert("Wait for 15 seconds and press OK");
    setTimeout(alertA, 10000 - (Date.now() - now)); // Subtract the time passed
},1000);

您可以将其包装在实用程序方法中:

function alertDelay(messages, timePassed) {
  if (typeof messages !== "object" || messages.length === 0) return;
  if (timePassed == null) timePassed = 0;
  var firstMessage = messages[0];
  var now = Date.now();
  setTimeout(function() {
    alert(firstMessage.message);
    alertDelay(messages.slice(1), Date.now() - now);
  }, firstMessage.delay - timePassed);
}

用法:

alertDelay([
  {message: 'Message 1', delay: 1000},
  {message: 'Message 2', delay: 5000},
  {message: 'Message 3', delay: 10000}
]);