You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Javascript迭代递归函数使用setTimeout计时不准确,node.js也存在该问题吗?

问题现象原因分析

第一个代码运行结果1200-1600ms的原因

你提供的第一份代码如下:

// Initialise the variables to check when the code started
let total_time = performance.now();
let recursions = 0;
function test() {
    recursions++;
    if (recursions === 60) {
        // Check how long it took to do 60 recursions
        console.log(`${performance.now() - total_time}ms`);
    }
// Timeout to call it 17ms later
setTimeout(() => test(), 17);
}
test();
  • 首先纠正一个误解:这段代码不是递归,是基于setTimeout的异步链式调用,不存在调用栈堆叠,setTimeout的作用是将下一次test的执行推入宏任务队列,等待当前执行栈清空后再调度执行。
  • 定时调度的固有开销:你设置的每次调度间隔是17ms,理想状态下60次调度总耗时是60*17=1020ms,但实际运行时每次调度都需要等待事件循环处理队列中的其他任务,累积下来会产生额外的开销。
  • 嵌套setTimeout的强制最小延迟限制:根据HTML规范,当setTimeout的嵌套层级超过5层时,浏览器会强制设置最小超时时间为4ms,不过你设置的17ms已经高于这个阈值,所以这部分影响很小。
  • 额外的浏览器限制:如果运行代码的标签页处于后台状态,多数浏览器会将setTimeout的最小调度间隔拉长到1000ms左右来降低功耗,这也是你可能得到1200-1600ms的核心原因之一,同时浏览器为了降低功耗会合并相近的定时器任务,也会增加额外的延迟。

第二个代码输出0ms的原因

你提供的测量单次耗时的代码如下:

let start_time = performance.now();
let total_time = performance.now();
let measure = 0;
function test() {
    start_time = performance.now();
    measure++;
    if (measure === 60) {
        console.log(`${performance.now() - total_time}ms`);
    }
    console.log(`Taken ${performance.now() - start_time}`);
}
test();
  • 你修改后的代码删除了setTimeout逻辑,test函数只会同步执行1次,永远不会触发measure===60的判断逻辑。
  • 函数内从给start_time赋值到打印差值的代码逻辑非常简单,执行耗时远低于performance.now()的时间分辨率(目前浏览器为了防范侧信道攻击,普遍将performance.now()的精度限制在1ms左右,低于这个精度的差值会返回0),所以最终得到0ms的结果是正常现象。

Node.js环境下的表现

  • 第一个代码在Node.js中运行不会有浏览器后台标签的大延迟限制,但Node.js同样存在嵌套setTimeout的最小延迟限制(嵌套层级超过5层后最小延迟为1ms),再加上事件循环的调度开销,最终总耗时会略高于1020ms,一般在1100ms左右,不会出现1600ms这类过高的结果。
  • 第二个代码在Node.js中运行同样会输出0ms,Node.js中performance.now()的精度同样不足以捕捉到简单同步代码的执行耗时。

内容的提问来源于stack exchange,提问作者Joy Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 01:06:03