想测一段代码跑了多久:做了个秒表工具,顺便搞懂计时精度
有回我想看一段脚本到底跑了 800 毫秒还是 1200 毫秒,随手 Date.now() 减了一下,结果两次跑出来差得离谱。后来才知道,Date.now() 精度不够量短间隔,正经该用 performance.now()。干脆写了个网页秒表,既能掐表又能分段计次,顺便把计时这档事弄明白。
一、为什么不用 Date.now()
Date.now() 返回的是"墙上时钟"时间,精度通常只到毫秒级,而且系统时间一旦被校时(NTP 同步)就会跳变。量长时间段还行,量几百毫秒内的短间隔,误差能到几十毫秒。
performance.now() 不一样:它从页面加载那一刻开始计时,精度到亚毫秒(小数位都有),而且不受系统时间调整影响——校时不会让它跳。测代码耗时、做动画、掐秒表,全该用它。
let start = 0, elapsed = 0, running = false;
function now() { return performance.now(); } // 高精度,单调不减
function startBtn() {
if (running) return;
start = now();
running = true;
requestAnimationFrame(loop);
}
二、暂停后怎么接着算
秒表要能暂停再续。关键点是:暂停时把已跑的时间存下来,再开始时只记新起点,而不是从零开始。
function pauseBtn() {
if (!running) return;
elapsed += now() - start; // 把这一段跑了的存起来
running = false;
}
function resumeBtn() {
if (running) return;
start = now(); // 新起点,elapsed 不动
running = true;
requestAnimationFrame(loop);
}
function loop() {
if (!running) return;
const total = elapsed + (now() - start);
display(total);
requestAnimationFrame(loop);
}
注意 loop 里显示的是 elapsed + (now() - start)——已存的时间加上当前这段。这样暂停了多少次都不会错。
三、计次分段(laps)
比赛中常要记"每一圈用了多久"。每点一次"计次",就记一个当前总时长。
let laps = [];
function lapBtn() {
const total = elapsed + (running ? now() - start : 0);
const prev = laps.length ? laps[laps.length - 1].total : 0;
laps.push({ total, split: total - prev }); // split 是这一段增量
renderLaps(laps);
}
split 是相邻两次计次的差,显示"这一圈比上一圈多用了多少"。

四、几个细节
| 点 | 说明 |
|---|---|
| 显示格式 | total 毫秒转成 分:秒.毫秒,Math.floor(total/60000) 取分 |
| 重置 | elapsed=0; laps=[]; running=false |
| 后台节流 | requestAnimationFrame 后台会停,但用的是 now()-start 差值,回来自动补正 |
| 长任务 | 跑几小时也没问题,performance.now() 上限够大 |
小结
秒表的核心不是 setInterval,是 performance.now()——它精度高、单调、不受校时影响,量短间隔比 Date.now() 靠谱得多。暂停靠"存已跑时间 + 记新起点"实现续跑,计次靠存每次总时长再算相邻差。后台节流不影响结果,因为显示永远基于时间差值而非累加。
- 点赞
- 收藏
- 关注作者
评论(0)