设置了超时时间但是不起作用,setTimeout有 bug?

举报
老码小张 发表于 2024/11/05 18:44:06 2024/11/05
【摘要】 你可能也遇到过这样的问题:写个setTimeout定时器,结果时间一长,浏览器就开始捣乱。比如你想要设置一个几小时甚至几天的延时,突然发现浏览器不听话了!这时候你就会想,难道浏览器的定时器是有上限的?没错,你没看错,setTimeout其实有个最大值限制,时间一超过这个值,就会出问题。别担心,今天咱们就来聊聊怎么解决这个问题,教你一招轻松搞定超长延时。为什么setTimeout会出问题?先来...

你可能也遇到过这样的问题:写个setTimeout定时器,结果时间一长,浏览器就开始捣乱。比如你想要设置一个几小时甚至几天的延时,突然发现浏览器不听话了!这时候你就会想,难道浏览器的定时器是有上限的?没错,你没看错,setTimeout其实有个最大值限制,时间一超过这个值,就会出问题。别担心,今天咱们就来聊聊怎么解决这个问题,教你一招轻松搞定超长延时。

图片

为什么setTimeout会出问题?

先来说说setTimeout的工作原理。你可以理解为它是一个“闹钟”,你告诉浏览器:“喂,过几秒钟叫我一下!”通常你设置的时间可能就是几秒、几十秒。但如果你设置个比如24小时的延时呢?这就是个大问题,因为浏览器的定时器有个时间上限。这个上限到底是多少呢?根据浏览器的不同实现,它通常是在2147483647毫秒,大概是24.8天左右

听起来时间已经很长了对吧?但如果你是开发一些长期运行的应用,比如倒计时工具,或者是一个需要在后台运行很久的任务,这个上限可能就远远不够了。一旦超过这个值,定时器可能会立刻触发,完全不按预期来。

举个栗子

假设你想设置一个48小时的延时:

setTimeout(() => {
  console.log("两天后提醒!");
}, 48 * 60 * 60 * 1000);

你觉得两天后会收到提醒?但实际上,因为48小时(即172800000毫秒)超过了最大值2147483647毫秒,这个定时器会立即执行。这种时候,很多开发者就会一头雾水,不知道哪里出了问题。

解决方法:分段计时

既然setTimeout有时间上限,我们可以考虑换个思路。既然一次不能跑完超长时间,那我们可以分段计时,把大时间分成多个小时间,逐步延时。

具体怎么操作呢?很简单,假设你要实现48小时的延时。我们可以先分成两段,每段24小时。每段时间到了之后,再启动下一段,这样就可以轻松实现超长延时。

来看个代码示例:

function setBigTimeout(callback, delay) {
  const maxDelay = 2147483647;

  if (delay > maxDelay) {
    setTimeout(() => {
      setBigTimeout(callback, delay - maxDelay);
    }, maxDelay);
  } else {
    setTimeout(callback, delay);
  }
}

在这个例子里,setBigTimeout函数帮你自动处理延时。如果你传入的delay超过了setTimeout的最大值,它会先延时maxDelay那么久,然后继续计算剩下的时间。这样一来,哪怕是48小时甚至更长时间的延时也能完美实现。

事实上,setBigTimeout 这个已经有一个工具可以直接用,你只需要 npm install 即可。

import { setBigTimeout } from "setbigtimeout";

const EIGHTY_FOUR_YEARS_IN_MILLISECONDS = 2.65e12;

setBigTimeout(() => {

  console.log("It's been 84 years...");

}, EIGHTY_FOUR_YEARS_IN_MILLISECONDS);

你真的需要这么长的延时吗?

我们在使用这个方法时,也要考虑一个问题:你是否真的需要这么长的延时?有时候,开发者可能因为设计上的原因,习惯了使用setTimeout来处理一些长期任务,但这其实并不是最好的选择。

很多时候,使用像 后台作业(background jobs)或者定时器服务(cron jobs) 可能是更好的方式。比如,如果你要让一个任务在几天后执行,或者你希望任务即使浏览器关闭后也能继续运行,那么你应该考虑在服务器端处理这个任务,而不是依赖前端的定时器。

浏览器的setTimeout设计初衷是为了处理短期任务,而非长期的调度。如果你在做的是一些需要长时间保持的任务,比如提醒、倒计时,或者需要在未来某个时间点完成的工作,使用服务端的定时器可能更稳妥。

如何避免“忘记的定时器”

在开发中,我们常常会遇到这样的问题:设置了一个定时器,结果在应用关闭或刷新后,定时器就被忘记了。这种时候,你可以通过一些持久化的存储方式来记录定时器的状态,比如本地存储(LocalStorage)或者是数据库。这样即使用户关闭了浏览器,下一次打开时,仍然可以从存储中取出之前的状态,继续任务。

来看一个简单的例子:

function startPersistentTimer(key, delay) {
  const startTime = Date.now();
  localStorage.setItem(key, startTime);

  setBigTimeout(() => {
    const savedStartTime = localStorage.getItem(key);
    if (savedStartTime && Date.now() - savedStartTime >= delay) {
      console.log("时间到了!");
    } else {
      console.log("定时器被取消或重置");
    }
  }, delay);
}

在这个例子中,我们将定时器的开始时间存到了localStorage中。这样,如果浏览器被刷新或者重启,仍然可以知道定时器的状态,并决定是继续还是取消任务。

思考

其实,处理超长延时并不复杂,关键是理解setTimeout的限制,然后找到合适的替代方案。通过分段计时和持久化存储,你可以轻松解决浏览器定时器的限制问题。当然,在某些情况下,使用服务器端的解决方案可能更适合你长期运行的任务。如果你对setTimeout有深入的了解你一定会知道setTimeout还不能设置超过一定的层级,这个层级是多少呢

希望今天的内容对你有帮助。如果你还遇到了其他定时器相关的问题,欢迎留言,我们一起探讨!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。