Window: setInterval() 方法

Baseline 广泛可用 *

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2015 年 7 月⁩以来,各浏览器均已提供此特性。

* 此特性的某些部分可能存在不同级别的支持。

Window 接口的 setInterval() 方法重复调用一个函数或执行一个代码片段,在每次调用之间都有固定的时间延迟。

语法

js
setInterval(code)
setInterval(code, delay)

setInterval(func)
setInterval(func, delay)
setInterval(func, delay, arg1)
setInterval(func, delay, arg1, arg2)
setInterval(func, delay, arg1, arg2, /* …, */ argN)

参数

func

一个 function,每隔 delay 毫秒执行一次。第一次执行发生在 delay 毫秒之后。

code

一个可选的语法允许你包含一个字符串而不是一个函数,该字符串每隔 delay 毫秒被编译和执行。出于与使用 eval() 造成安全风险相同的原因,不推荐使用此语法。

delay 可选

计时器在每次执行指定函数或代码之间应该延迟的时间,单位为毫秒(千分之一秒)。如果未指定,则默认为 0。有关 delay 值的允许范围的详细信息,请参阅下面的延迟限制

arg1, …, argN 可选

计时器到期后,将传递给 func 指定函数的附加参数。

返回值

setInterval() 方法返回一个正整数(通常在 1 到 2,147,483,647 之间),该整数唯一标识通过调用创建的间隔计时器。此标识符(通常称为“间隔 ID”)可以传递给 clearInterval() 以停止指定函数的重复执行。

在同一个全局环境(例如,特定的窗口或 Worker)中,只要原始计时器仍然处于活动状态,间隔 ID 就被确保保持唯一,并且不会为任何新的间隔计时器重复使用。然而,不同的全局环境维护它们自己独立的间隔 ID 池。

请注意,setInterval()setTimeout() 共享相同的 ID 池,并且 clearInterval()clearTimeout() 在技术上可以互换使用。然而,为了清晰起见,你应该始终尝试将它们匹配起来,以避免在维护代码时产生混淆。

注意: delay 参数被转换为一个有符号的 32 位整数。这有效地将 delay 限制在 2147483647 毫秒(大约 24.8 天),因为它在 IDL 中被指定为有符号整数。

示例

示例 1:基本语法

以下示例演示了 setInterval() 的基本语法。

js
const intervalID = setInterval(myCallback, 500, "Parameter 1", "Parameter 2");

function myCallback(a, b) {
  // Your code here
  // Parameters are purely optional.
  console.log(a);
  console.log(b);
}

示例 2:交替两种颜色

以下示例每秒调用 flashtext() 函数一次,直到按下停止按钮。

HTML

html
<div id="my_box">
  <h3>Hello World</h3>
</div>
<button id="start">Start</button>
<button id="stop">Stop</button>

CSS

css
.go {
  color: green;
}
.stop {
  color: red;
}

JavaScript

js
// variable to store our intervalID
let intervalId;

function changeColor() {
  // check if an interval has already been set up
  intervalId ??= setInterval(flashText, 1000);
}

function flashText() {
  const oElem = document.getElementById("my_box");
  oElem.className = oElem.className === "go" ? "stop" : "go";
}

function stopTextColor() {
  clearInterval(intervalId);
  // release our intervalId from the variable
  intervalId = null;
}

document.getElementById("start").addEventListener("click", changeColor);
document.getElementById("stop").addEventListener("click", stopTextColor);

结果

“this”问题

当你将一个方法传递给 setInterval() 或任何其他函数时,它会以错误的 this 值被调用。这个问题在 JavaScript 参考中有详细解释。

解释

setInterval() 执行的代码运行在与调用它的函数不同的执行上下文。因此,被调用函数的 this 关键字被设置为 window(或 global)对象,它与调用 setTimeout 的函数的 this 值不同。请参见以下示例(它使用 setTimeout() 而不是 setInterval() ——实际上,对于这两个计时器来说,问题是相同的)

js
myArray = ["zero", "one", "two"];

myArray.myMethod = function (sProperty) {
  alert(arguments.length > 0 ? this[sProperty] : this);
};

myArray.myMethod(); // prints "zero,one,two"
myArray.myMethod(1); // prints "one"
setTimeout(myArray.myMethod, 1000); // prints "[object Window]" after 1 second
setTimeout(myArray.myMethod, 1500, "1"); // prints "undefined" after 1.5 seconds

// Passing the 'this' object with .call won't work
// because this will change the value of this inside setTimeout itself
// while we want to change the value of this inside myArray.myMethod.
// In fact, it will be an error because setTimeout code expects this to be the window object:
setTimeout.call(myArray, myArray.myMethod, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object"
setTimeout.call(myArray, myArray.myMethod, 2500, 2); // same error

如你所见,在传统 JavaScript 中,没有办法将 this 对象传递给回调函数。

一种可能的解决方案

所有现代 JavaScript 运行时(在浏览器和其他地方)都支持带有词法 this箭头函数——允许我们写 setInterval(() => this.myMethod()),如果我们在 myArray 方法内部。

如果你需要支持 IE,请使用 Function.prototype.bind() 方法,它允许你指定应该用作给定函数所有调用的 this 值。这可以让你轻松绕过 this 值不明确的问题,具体取决于调用函数的上下文。

用法说明

setInterval() 函数通常用于为反复执行的函数(例如动画)设置延迟。你可以使用 clearInterval() 取消间隔。

如果你希望在指定延迟之后 调用一次函数,请使用 setTimeout()

延迟限制

间隔可以嵌套;也就是说,setInterval() 的回调可以反过来调用 setInterval() 来启动另一个间隔运行,即使第一个间隔仍在运行。为了减轻这可能对性能造成的影响,一旦间隔嵌套超过五层深,浏览器将自动强制间隔的最小值为 4 毫秒。在深度嵌套的 setInterval() 调用中尝试指定小于 4 毫秒的值将被固定为 4 毫秒。

在某些情况下,浏览器可能会对间隔强制执行更严格的最小值,尽管这些情况不常见。另请注意,回调之间实际经过的时间可能比给定的 delay 长;有关示例,请参阅延迟时间长于指定时间的原因

确保执行持续时间短于间隔频率

如果你的逻辑执行时间可能比间隔时间长,建议使用 setTimeout() 递归调用一个命名函数。例如,如果使用 setInterval() 每 5 秒轮询一次远程服务器,网络延迟、无响应的服务器以及许多其他问题可能会阻止请求在指定时间内完成。因此,你可能会发现自己有排队的 XHR 请求,它们不一定会按顺序返回。

在这些情况下,首选递归 setTimeout() 模式

js
(function loop() {
  setTimeout(() => {
    // Your logic here

    loop();
  }, delay);
})();

在上面的代码片段中,声明并立即执行了一个命名函数 loop()。在逻辑完成执行后,loop()setTimeout() 内部被递归调用。虽然这种模式不保证在固定间隔执行,但它确实保证在上一个间隔完成之前进行递归。

规范

规范
HTML
# dom-setinterval-dev

浏览器兼容性

另见