HOWTO · Node.js

Node JS 中的 settimeout

本教程演示如何在 node js 中使用 settimeout 方法

本页内容

setTimeout 是 Node.js 提供的全局函数,用于延迟调度回调。它会立即返回定时器句柄,因此回调等待运行时,当前 JavaScript 代码可以继续执行。它适用于延后处理、有明确边界的重复任务,或可能需要取消的任务。延迟时间只是回调具备执行资格的门槛,并不保证回调会在某个确切时刻运行:繁忙的同步任务和事件循环活动都可能让回调更晚执行。

setTimeout 在 Node.js 中的作用

调用时需要传入回调和以毫秒为单位的延迟时间:setTimeout(callback, delay)。延迟值会先转换为数字;小于 1 的值会被 Node.js 视为 1 毫秒。回调会在事件循环的后续轮次中运行,不会中断正在执行的代码。返回值是一个定时器句柄,可以传给 clearTimeout。

需要稍后执行任务时,定时器很有用,但它不是精确的时钟,也不会让占用大量 CPU 的工作变成非阻塞任务。如果 JavaScript 正忙,回调必须等到运行时能够处理它时才会执行。对于重复任务,可以在当前回调完成后再安排下一次执行,这样任务就有明确的停止时机。

延迟后运行回调

此示例只调度一次回调。第一条和最后一条消息由当前同步代码打印;定时器达到可执行条件后,回调才会运行。下面的示例代码和输出保留英文,输出展示了这三条消息的先后顺序。

运行此示例:

本节中的代码以及显示的标签或输出仍为英文。

console.log("Start");
setTimeout(() => {
  console.log("Callback ran");
}, 10);
console.log("End");
Start
End
Callback ran

同步消息会先于定时器回调出现,因为脚本先执行完当前语句。回调实际开始运行的时间可能晚于请求的延迟,具体取决于事件循环还需要处理哪些任务。不要把定时器当作其他操作已经完成的保证;需要协调异步任务时,应使用该操作发出的完成信号。

理解回调的时机与执行顺序

延迟时间决定了 Node.js 最早何时可以让定时器回调具备运行资格,但不会为回调预留精确的执行时刻。如果同步工作占用线程,超过了这个时刻,回调就只能等到这段工作让出执行权后才能运行。同样,进程繁忙时,延迟不同的两个定时器也可能比请求的时间更晚执行。理解同步语句和事件循环的执行顺序,不要把延迟时间当作精确的日程安排。

如果回调要读取其他代码修改的状态,这一区别就很重要。当正确性取决于一个任务先于另一个任务完成时,应通过 Promise、回调或其他完成信号明确表达这种依赖。setTimeout 只负责调度回调,不会等待网络、文件或数据库操作完成。

重复执行并安全停止

要重复执行某项操作,可以在回调内部安排下一次执行,并跟踪停止条件。这个有限次重复的示例会输出三条消息,随后不再安排后续任务。代码和输出中的标签保留英文;输出展示了每次运行的编号。

本节中的代码以及显示的标签或输出仍为英文。

let runs = 0;

function repeatMessage() {
  runs += 1;
  console.log(`Run ${runs}`);
  if (runs < 3) {
    setTimeout(repeatMessage, 100);
  }
}

setTimeout(repeatMessage, 100);
Run 1
Run 2
Run 3

这个条件可以防止函数不断调度自身。如果还需要根据用户输入或应用状态停止重复任务,请保存定时器句柄,并在停止时清除待执行的任务。如果固定间隔的任务不能彼此重叠,那么等当前工作完成后再安排下一次定时器,通常比按固定节奏启动每次任务更容易控制。

取消尚未触发的定时器

如果已调度的回调之后可能不再需要,请保存 setTimeout 返回的值,并在回调运行前将这个句柄传给 clearTimeout。示例代码和输出标签保留英文;输出说明定时器已取消:

本节中的代码以及显示的标签或输出仍为英文。

const timeoutHandle = setTimeout(() => {
  console.log("This callback is canceled");
}, 1000);

clearTimeout(timeoutHandle);
console.log("Timeout canceled");

clearTimeout 可以取消尚未触发的定时器。如果回调已经开始运行,清除句柄无法撤销它已经完成的工作。对未知句柄或已清除的句柄调用 clearTimeout 不会造成问题,因此可以简化清理流程。在生命周期较长的代码中,应在所属操作结束时取消相关定时器,避免回调使用过期状态。