Internet explorer 8 IE8 setInterval和setTimeout在正常运行49天后立即启动

Internet explorer 8 IE8 setInterval和setTimeout在正常运行49天后立即启动,internet-explorer-8,settimeout,setinterval,Internet Explorer 8,Settimeout,Setinterval,当Windows系统的正常运行时间接近49.7天时,内部Windows毫秒刻度计数器接近2^32。 在计算何时触发setInterval或setTimeout事件时,InternetExplorer8中的错误似乎存在算术溢出。例如,如果您在正常运行时间的第49天,请致电 setInterval(func, 86400000); // fire event in 24 hours func将立即调用,而不是在24小时内调用 如果向setInterval或setTimeout传递的数据足够大,则此

当Windows系统的正常运行时间接近49.7天时,内部Windows毫秒刻度计数器接近2^32。 在计算何时触发setInterval或setTimeout事件时,InternetExplorer8中的错误似乎存在算术溢出。例如,如果您在正常运行时间的第49天,请致电

setInterval(func, 86400000); // fire event in 24 hours
func将立即调用,而不是在24小时内调用

如果向setInterval或setTimeout传递的数据足够大,则此错误可能会在25天正常运行时间(2^31毫秒)后的任何时间发生。(不过我只在第49天检查过。)

您可以通过在命令行中输入“net statistics server”来检查正常运行天数


有解决方法吗?

您可以使用
setTimeout

function setSafeTimeout(func, delay){
    var target = +new Date + delay;
    return setTimeout(function(){
        var now = +new Date;
        if(now < target) {
            setSafeTimeout(func, target - now);
        } else {
            func();
        }
    }, delay);
}
函数setSafeTimeout(函数,延迟){
var目标=+新日期+延迟;
返回setTimeout(函数(){
var now=+新日期;
如果(现在<目标){
设置安全输出(函数,目标-现在);
}否则{
func();
}
},延误);
}

这仍然会返回
setTimeout
中的值,因此,如果未遇到错误,仍然可以使用
cleartimout
。如果
clearTimeout
需要防弹,或者您需要
setInterval
(大概是
clearTimeout
),您需要在问题上抛出更多的代码,但在执行
func
之前验证足够时间的原则仍然成立。

卡梅隆·乔丹的答案的一个变体:

function setSafeTimeout(func, delay) {
    var target = +new Date + delay;
    var helper = function() {
        var now = +new Date;
            if (now < target) {
                setTimeout(arguments.callee, 1000);
            } else {
                func();
            }
        }
    return setTimeout(helper, delay);
}
函数setSafeTimeout(函数,延迟){
var目标=+新日期+延迟;
var helper=函数(){
var now=+新日期;
如果(现在<目标){
setTimeout(arguments.callee,1000);
}否则{
func();
}
}
返回setTimeout(helper,delay);
}
helper函数的目的是在IE8处于错误状态时每秒调用自己一次

一个有用的测试工具是(不过,仅限Windows XP)。例如,将新的tick count设置为0xffff0000,将在tick计数器滚动之前为您提供65秒的错误行为。任何设置为120秒的计时器都无法正常启动


此外,在Windows XP中,错误的setTimeout行为似乎与单击鼠标左键有关。

此项目帮助解决了我一直在开发的应用程序中的一个主要难题,非常感谢发布此项目。AdjustTickCount实用程序是证明解决方案的一个非常重要的工具,因此对此也表示赞许

这个问题也会影响IE7,而且似乎也会影响IE6,但更糟糕的后果是浏览器停止响应,而且解决方案似乎也无法在该版本上运行。这些旧版本的用户仍然很多,特别是在商业/企业界

我没有发现鼠标左键是WindowsXP中的一个因素,没有它问题就会出现

如果超时延迟最多是几秒钟,并且应用程序中只设置了非常少量的超时,那么前两个答案就可以了。如果需要更多和更长的超时时间,那么还有更多的工作要做,以防止web应用程序变得不可用。在使用框架(如用户)的Web2.0RIA中,用户可能会让它运行数天,因此产生动画或其他短暂效果所需的超时时间可能比两个半秒的延迟时间更长

第一个解决方案是一个良好的开端,但将下一个超时设置为“立即目标”将再次导致立即调用该函数,因为这仍然会使正常运行时间+延迟超过2^32毫秒,因此JS代码将旋转,直到正常运行时间变为0(或用户终止浏览器)

第二种解决方案是一种改进,因为过早超时只会以1秒的间隔出现,直到现在,这是正常运行时间结束后1秒内的事情,允许其他代码查看,但实验表明,如果有足够多的等待超时,这仍然足以使浏览器无法使用。而且它仍将继续,直到正常运行时间结束,因此,如果请求的延迟足够长,用户仍可能决定关闭浏览器

一个不太需要CPU的解决方案是将每个后续超时的延迟设置为前一个延迟持续时间的一半,直到该延迟小于500ms,此时我们知道正常运行时间的结束点即将到来(<1秒)我们可以将下一个超时设置为
target now
,这样过早的超时检查只需再进行少量的循环即可停止。达到这一点所需的时间将取决于原始延迟的时间以及调用
setSafeTimeout
时正常运行时间的结束时间,但最终在CPU负载最小的情况下,应用程序恢复正常运行,而用户不会经历任何长时间的减速

大概是这样的:

function setSafeTimeout(func, delay) {
  var target = +new Date + delay;
  var newDelay = delay;
  var helper = function()
  {
    var now = +new Date;
    if (now < target)
    {
      newDelay /= 2; // halve the wait time and try again
      if(newDelay < 500) // uptime wrap around is imminent
      {
        newDelay = target-now; // go back to using original target
      }
      var handle = setTimeout(helper, newDelay);
      // if required record handle somewhere for clearTimeout
    }
    else
    {
      func();
    }
  };
  return setTimeout(helper, delay);
};

有问题吗?你是在寻求解决办法还是与世界分享?所以。。。你基本上是说,在系统正常运行25天后超时24小时会出现bug?我想知道我什么时候需要这样的超时?这更多的是记录IE8中的一个bug。例如,这可能是一个问题,假设一个计时器在一小时后启动,并在启动时将用户重定向到另一个页面。当您的正常运行时间在2^32毫秒的一小时内时,计时器将立即启动,用户将无法访问原始页面。一旦正常运行时间超过2^32毫秒,一切都将重新开始工作。但在这一小时内,页面将被破坏。你应该添加一个解决方案请求,这样实际上会有一个问题。这应该在IE9测试版中得到解决。谢谢,我会投票支持这个问题,但作为新帐户持有人,我是p
function setSafeTimeout(func, delay) {
  var target = +new Date + delay;
  var newDelay = delay;
  var helper = function()
  {
    var now = +new Date;
    if (now < target)
    {
      var timeToTarget = target-now;
      newDelay /= 2; // halve the wait time and try again
      if(newDelay < 500 || newDelay > timeToTarget) // uptime wrap around is imminent
      {
        newDelay = timeToTarget; // go back to using original target
      }
      var handle = setTimeout(helper, newDelay);
      // if required record handle somewhere for clearTimeout
    }
    else
    {
      func();
    }
  };
  return setTimeout(helper, delay);
};