探索 JavaScript 定时器

setTimeout()

在编写 JavaScript 代码时,你可能想要延迟一个函数的执行。

这就是 setTimeout 的作用。你指定一个稍后要执行的回调函数,以及一个表示要延迟多久的值,单位为毫秒。

(() => {
  // runs after 2 seconds
}, 2000);

(() => {
  // runs after 50 milliseconds
}, 50);

此语法定义了一个新函数。你可以在其中调用任何其他你想要的函数,或者你可以传递一个已存在的函数名和一组参数。

const  = (, ) => {
  // do something
};

// runs after 2 seconds
(, 2000, firstParam, secondParam);

在 Node.js 中,setTimeout 返回一个 Timeout 实例,而在浏览器中它返回一个数字定时器 ID。这个对象或 ID 可以用来取消已计划的函数执行。

const  = (() => {
  // should run after 2 seconds
}, 2000);

// I changed my mind
();

零延迟

如果你将超时延迟指定为 0,回调函数将尽快执行,但在当前函数执行之后。

(() => {
  .('after ');
}, 0);

.(' before ');

这段代码将打印:

before
after

这对于避免在密集型任务上阻塞 CPU 特别有用,它可以通过在调度器中将函数排队,让其他函数在执行繁重计算时得以执行。

一些浏览器(IE 和 Edge)实现了一个 setImmediate() 方法,它实现了完全相同的功能,但它不是标准的,并且在其他浏览器上不可用。但它在 Node.js 中是一个标准函数。

setInterval()

setInterval 是一个类似于 setTimeout 的函数,但有一个区别:它不是只运行一次回调函数,而是会以你指定的特定时间间隔(以毫秒为单位)永远运行它。

(() => {
  // runs every 2 seconds
}, 2000);

上面的函数每 2 秒运行一次,除非你使用 clearInterval 告诉它停止,并向其传递 setInterval 返回的 interval id。

const  = (() => {
  // runs every 2 seconds
}, 2000);

();

通常会在 setInterval 回调函数内部调用 clearInterval,让它自动判断是应该再次运行还是停止。例如,这段代码会一直运行某个东西,除非 App.somethingIWait 的值变为 arrived

const  = (() => {
  if (App.somethingIWait === 'arrived') {
    ();
  }
  // otherwise do things
}, 100);

递归 setTimeout

setInterval 每 n 毫秒启动一个函数,完全不考虑函数何时完成执行。

如果一个函数总是花费相同的时间,那就没问题。

setInterval working fine

但也许函数会因网络状况等原因而有不同的执行时间。

setInterval varying duration

甚至可能一次长时间的执行会与下一次执行重叠。

setInterval overlapping

为了避免这种情况,你可以在回调函数完成时安排一个递归的 setTimeout 来调用。

const  = () => {
  // do something

  (, 1000);
};

(, 1000);

以实现这种场景

Recursive setTimeout

在 Node.js 中,通过 Timers 模块 提供了 setTimeoutsetInterval

Node.js 还提供了 setImmediate(),它等同于使用 setTimeout(() => {}, 0),主要用于与 Node.js 事件循环协同工作。