【问题标题】:setTimeout fires immediately if the delay more than 2147483648 milliseconds如果延迟超过 2147483648 毫秒,setTimeout 立即触发
【发布时间】:2013-04-25 06:00:33
【问题描述】:

问题

如果delay 超过 2147483648 毫秒(24.8551 天),该函数将立即触发。

示例

setTimeout(function(){ console.log('hey') }, 2147483648) // this fires early
setTimeout(function(){ console.log('hey') }, 2147483647) // this works properly

我在 Chrome v26 和 Node.js v8.21 下试过

【问题讨论】:

标签: javascript node.js delay settimeout


【解决方案1】:

setTimeout 的上限为0x7FFFFFFF(或十进制的2147483647

这是因为 setTimeout 使用一个 32 位整数来存储它的延迟值,所以任何超过这个值都会导致问题

如果您想要在 X 天后触发的超时,您可以尝试使用 setInterval 来代替像这样的较低延迟值

function setDaysTimeout(callback,days) {
    // 86400 seconds in a day
    var msInDay = 86400*1000; 

    var dayCount = 0;
    var timer = setInterval(function() {
        dayCount++;  // a day has passed

        if(dayCount == days) {
           clearInterval(timer);
           callback.apply(this,[]);
        }
    },msInDay);
}

然后你会像这样使用它

setDaysTimeout(function() {
     console.log('Four days gone');
},4); // fire after 4 days

【讨论】:

  • 天哪,这很糟糕,我怎样才能让超时时间更长?
  • @Adam 我使用setInterval 更新了答案,延迟更小(而不是setTimeout
  • 这不是很好,因为间隔可能不会在您想要的时候准确触发。更好的是计算您希望事件发生的时间,然后设置一个间隔(如果 >= 1 天)或超时(如果
  • 一天是 31,556,952,000ms/365。对于更长的时间,我可能会考虑使用平均一天的长度。
【解决方案2】:

由于您被限制为 32 位,因此只需将 setTimeout 包装在递归函数中,如下所示:

function setLongTimeout(callback, timeout_ms)
{

 //if we have to wait more than max time, need to recursively call this function again
 if(timeout_ms > 2147483647)
 {    //now wait until the max wait time passes then call this function again with
      //requested wait - max wait we just did, make sure and pass callback
      setTimeout(function(){ setLongTimeout(callback, (timeout_ms - 2147483647)); },
          2147483647);
 }
 else  //if we are asking to wait less than max, finally just do regular setTimeout and call callback
 {     setTimeout(callback, timeout_ms);     }
}

这不是太复杂,应该可以扩展到javascript数字的限制,即1.7976931348623157E+10308,到那个毫秒数,我们都将死去。

为了让你能够设置LongTimeout,你可以修改函数以接受通过引用传递的对象,从而将范围保留回调用函数:

function setLongTimeout(callback, timeout_ms, timeoutHandleObject)
{
 //if we have to wait more than max time, need to recursively call this function again
 if(timeout_ms > 2147483647)
 {    //now wait until the max wait time passes then call this function again with
      //requested wait - max wait we just did, make sure and pass callback
      timeoutHandleObject.timeoutHandle = setTimeout(function(){ setLongTimeout(callback, (timeout_ms - 2147483647), timeoutHandleObject); },
          2147483647);
 }
 else  //if we are asking to wait less than max, finally just do regular setTimeout and call callback
 {     timeoutHandleObject.timeoutHandle = setTimeout(callback, timeout_ms);     }
}

现在您可以调用超时,然后在需要时取消它:

var timeoutHandleObject = {};
setLongTimeout(function(){ console.log("Made it!");}, 2147483649, timeoutHandleObject);
setTimeout(function(){ clearTimeout(timeoutHandleObject.timeoutHandle); }, 5000);

【讨论】:

  • 这个函数的问题是实现一个clearLongTimeout函数很别扭。
  • 很公平,查看我的编辑以添加取消超时的功能,而不是增加太多复杂性来完成它
  • 您的新函数存在竞争条件。如果发生这一系列事件,它将无法正常工作: 1. 客户端读取timeoutHandle 值。 2. 超时发生,setLongTimeout 函数重置超时。 3.客户端使用timeoutHandle句柄清除(已经触发的)超时。
  • 要解决这个问题,而不是接受timeoutHandleObject,而是返回一个可用于清除超时的函数。然后返回的函数可以访问您的内部变量,并且可以使用对象的正确状态。
  • 如果调用函数将 timeoutHandleObject.timeoutHandle 存储在另一个变量中并让 timeoutHandleObject 超出范围,那不只是竞争条件吗?我在示例中演示的方式是,当您想要清除超时时,您可以使用对象并显式引用成员变量。我很确定只要你在你的范围内保留 timeoutHandleObject,你就没有竞争条件。但我确实理解你的意思,如果你抓住 timeoutHandleObject.timeoutHandle 你以后可能会失去取消超时的能力。
猜你喜欢
  • 2012-06-08
  • 2011-10-27
  • 1970-01-01
  • 2015-07-12
  • 2017-07-24
  • 2013-08-08
  • 1970-01-01
  • 1970-01-01
  • 2020-08-22
相关资源
最近更新 更多