Skip to content

重复的定时器 ​

本节目标:理解“重复的定时器”的核心思路,并能把它用于实际开发或面试表达。

说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。

使用setinterval()创建的定时器确保了定时器代码规则地插入队列中。

这个方式的问题在于,定时器代码可能在代码再次被添加到队列之前还没有完成执行,结果导致定时器代码连续运行好几次,而之间没有任何停顿。幸好,JavaScript引擎够聪明,能避免这个问题。当setinterval()时,仅当没有该定时器的任何其他代码实例时,才将定时器代码添加到队列中。这确保了定时器代码加入到队列中的最小时间间隔为指定间隔。

这种重复定时器的规则有两个间题: ⑴某些间隔会被跳过; (2)多个定时器的代码执行之间的间隔可能会比预期的小。

假设,某个onclick事件处理程序使用setinterval()200ms间隔的重复定时器。如果事件处理程序花了 300ms多一点的时间完成,同时定时器代码也花了差不多的时间,就会同时出现跳过间隔且连续运行定时器代码的情况。如下图:

这个例子中的第1个定时器是在205ms处添加到队列中的,但是直到过了300ms处才能够执行。当执行这个定时器代码时,在405ms处又给队列添加了另外一个副本。在下一个间隔,即605ms处,第一个定时器代码仍在运行,同时在队列中已经有了一个定时器代码的实例。结果是,在这个时间点上的定时器代码不会被添加到队列中。结果在5ms处添加的定时器代码结束之后,405ms处添加的定时器代码就立刻执行。
为了避免setinterval()重复定时器的这2个缺点,你可以用如下模式使用链式setTimeout()
setTimeout(function () {
    // 处理中
    setTimeout(arguments.callee, interval);
}, interval);
这个模式链式调用了setTimeout(),每次函数执行的时候都会创建一个新的定时器。第二个
setTimeout()调用使用了arguments.callee来获取对当前执行的函数的引用,并为其设置另外一个定时器。这样做的好处是,在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔。而且,它可以保证在下一次定时器代码执行之前,至少要等待指定的间隔,避免了连续的运行。这个模式主要用于重复定时器,如下例所示。
setTimeout(function () {
    var div = document.getElementById("myDiv");
    left = parseInt(div.style.left) + 5;
    div.style.left = left + "px";
    if (left < 200) {
        setTimeout(arguments.callee, 50);
    }
}, 50);
这段定时器代码每次执行的时候将一个<div>元素向右移动,当左坐标在200像素的时候停止。
JavaScript动画中使用这个模式很常见。

每个浏览器窗口口、标签页、或者frame有其各自的代码执行队列。这意味着进行跨frame或者跨窗口的定时调用,当代码同时执行的时候可能会导致竞争条件。无论何时常需要使用这种通信类型,最好是在接收frame或者窗口中创建一个定时器来执行代码。

本站总访问 — 次