[译]可以自纠正的setInterval代替方法

本文探讨了如何改进JavaScript setInterval的执行效率,通过一个自校正的替代方案,确保代码在预设的时间间隔内准确执行,同时分析了CPU优先级和性能影响。
最近,我在做一些有关setInterval方法的尝试.对于初学者来说,setInterval是可以让你在指定的时间间隔里重复执行某段代码的方法。
比如,你可以用下面的代码创建一个每秒执行的时间间隔
setInterval(function() {
    console.log('I execute every second!');
}, 1000);

受JavaScript单线程机制的影响,setInterval和它的表哥setTimeout都是会延迟执行的。当你打算创建1000毫秒执行一次的时间间隔,实际上可能需要超过1000毫秒才能触发一次。通常只会有几毫秒的延迟,看起来似乎是一个微不足道的问题。但也许你会希望能够在之后的循环里能够努力纠正这个时间误差,把这个时间间隔控制到预期的效果。换句话说,如果代码执行时间(假定延迟7毫秒)和设定的时间间隔一共用了1007毫秒,你会尽量将下一个执行发生的时间安排在接近2000毫秒(即993毫秒后)。
但由于浏览器实现的差异,事实上执行时间可能并不是你预期的那样。我们可以通过跟踪setInterval里的时间戳来看看。

如果你在Chrome,Safari,Internet Explorer,或Node.js的当前版本运行这段代码,你会发现时间间隔会随着执行次数的增加同步增加。
var startTime = Date.now();
setInterval(function() {
  console.log((Date.now() - startTime) + 'ms elapsed');
}, 1000);

在我的测试,我发现只有Firefox试图保持间隔执行同步。
不管这是否是setInterval预期的行为,我需要这个时间间隔尽可能的按设定的时间间隔来执行。下面是我对这个问题的解决方案:
window.setCorrectingInterval = (function(func, delay) {
  var instance = { };

  function tick(func, delay) {
    if (!instance.started) {
      instance.func = func;
      instance.delay = delay;
      instance.startTime = new Date().valueOf();
      instance.target = delay;
      instance.started = true;

      setTimeout(tick, delay);
    } else {
      var elapsed = new Date().valueOf() - instance.startTime,
        adjust = instance.target - elapsed;

      instance.func();
      instance.target += instance.delay;

      setTimeout(tick, instance.delay + adjust);
    }
  };

  return tick(func, delay);
});

上面的代码将在window宿主下创建一个setCorrectingInterval全局函数,它的形参是和setInterval相同的

下面我们列一下在这个方法里我们到底做了什么:
1.在闭包环境里,包装了一个tick方法,用以跟踪时间间隔对象的实例
2.当方法被第一次调用时(相当于instance.started为False),在instance对象里初始化一些必须的属性
3.因setInterval的执行间隔本身是不可控的(译者注),我们使用setTimeout来代替。配合tick及调节过的延迟时间来循环调用。
4.通过计算上次函数被调用的时间与当前时间的间隔加以计算,预期下次的延迟。


我们可以使用setcorrectinginterval来代替使用setInterval来达到我们的目地。
var startTime = Date.now();
setCorrectingInterval(function() {
  console.log((Date.now() - startTime) + 'ms elapsed');
}, 1000);

下图中可以看出,执行的时间间隔并没有不断增加,而是尽可能的将代码控制在每秒执行一次。


译者注:
写这个文章的人一定很但蛋疼,这篇博文的信息量,个人感觉不如它的评论大。评论中提到了CPU优先级及性能问题,可以看看。
The main problem with this solution is that setInterval has a higher CPU priority than setTimeout or requestAnimationFrame. So the browser will prioritize these calls in the event loop and it _MAY_ cause performance issues in other areas of the site depending on what you're doing. That said, if you have a site that just displays a clock I'm sure you're fine :)
一个老兄的评论:
这种方法的主要问题是,setIntervalsetTimeout及requestanimationframe较高的CPU优先级。所以浏览器在处理调用的事件循环时,可能会引起性能问题。

邮箱:simon4545##126.com
原文链接:http://www.andrewduthie.com/post/a-self-correcting-setinterval-alternative/?utm_source=javascriptweekly&utm_medium=email
这是我第一次翻译外文,水平有限,我连CET-4都没有过,如果有不对之处,欢迎指正
在TypeScript中,虽然`setInterval`是一个常用的方法来实现定时任务,但它也有一些缺点,比如难以管理定时器的取消。为了更方便地管理定时任务,TypeScript(以及JavaScript)提供了一些替代方法: 1. **`setTimeout`递归调用**:可以使用`setTimeout`来模拟`setInterval`的行为,通过在回调函数中递归调用`setTimeout`来实现定时任务。这种方法的好处是可以更灵活地控制每次调用的间隔时间。 ```typescript function mySetInterval(callback: () => void, delay: number): () => void { const timerId = setTimeout(function tick() { callback(); setTimeout(tick, delay); }, delay); return () => clearTimeout(timerId); } // 使用示例 const cancelInterval = mySetInterval(() => { console.log('Hello, world!'); }, 1000); // 取消定时任务 cancelInterval(); ``` 2. **`requestAnimationFrame`**:如果你的任务是需要在浏览器的每一帧都执行一次的动画,可以使用`requestAnimationFrame`。它比`setInterval`更高效,因为它会在浏览器的重绘周期中执行。 ```typescript function animate(callback: () => void): () => void { const start = performance.now(); function animateFrame(timestamp: number) { callback(); requestAnimationFrame(animateFrame); } requestAnimationFrame(animateFrame); return () => cancelAnimationFrame(start); } // 使用示例 const cancelAnimation = animate(() => { console.log('Animating...'); }); // 取消动画 cancelAnimation(); ``` 3. **第三方库**:还有一些第三方库如`lodash`提供的`throttle`和`debounce`方法,可以用于控制函数的执行频率。 ```typescript import { throttle } from 'lodash'; const throttled = throttle(() => { console.log('Throttled function'); }, 1000); // 使用示例 throttled(); ``` 这些方法各有优缺点,具体选择哪种方法取决于你的实际需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值