1. 自校正定时器 (Self-Correcting Timer)
这是最常用且效果最好的解决方案:
javascript
class AccurateTimer {
constructor(callback, interval) {
this.callback = callback;
this.interval = interval;
this.expected = null;
this.timeout = null;
this.isRunning = false;
}
start() {
if (this.isRunning) return;
this.isRunning = true;
this.expected = Date.now() + this.interval;
this.timeout = setTimeout(() => this.step(), this.interval);
}
step() {
if (!this.isRunning) return;
const drift = Date.now() - this.expected;
// 执行回调
this.callback();
// 计算下次执行时间,补偿时间偏差
this.expected += this.interval;
const nextDelay = Math.max(0, this.interval - drift);
this.timeout = setTimeout(() => this.step(), nextDelay);
}
stop() {
this.isRunning = false;
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
}
}
// 使用示例
const timer = new AccurateTimer(() => {
console.log('精确执行:', new Date().toISOString());
}, 1000);
timer.start();
2. 基于 performance.now() 的高精度计时
javascript
class HighPrecisionTimer {
constructor(callback, interval) {
this.callback = callback;
this.interval = interval;
this.startTime = null;
this.expectedTime = null;
this.isRunning = false;
this.frameId = null;
}
start() {
if (this.isRunning) return;
this.isRunning = true;
this.startTime = performance.now();
this.expectedTime = this.startTime + this.interval;
this.tick();
}
tick() {
if (!this.isRunning) return;
const currentTime = performance.now();
if (currentTime >= this.expectedTime) {
// 执行回调
this.callback();
// 设置下次预期执行时间
this.expectedTime += this.interval;
}
// 继续检查
this.frameId = requestAnimationFrame(() => this.tick());
}
stop() {
this.isRunning = false;
if (this.frameId) {
cancelAnimationFrame(this.frameId);
this.frameId = null;
}
}
}
3. Web Workers 方案
graph LR
A[主线程] -->|发送计时请求| B[Web Worker]
B -->|独立计时| C[高精度计时]
C -->|时间到达| D[发送消息]
D -->|执行回调| A
style B fill:#e1f5fe
style C fill:#fff3e0
主线程代码:
javascript
class WorkerTimer {
constructor(callback, interval) {
this.callback = callback;
this.interval = interval;
this.worker = null;
this.isRunning = false;
}
start() {
if (this.isRunning) return;
// 创建 Worker
const workerCode = `
let timerId = null;
let startTime = null;
let interval = null;
let expectedTime = null;
self.onmessage = function(e) {
const { action, intervalMs } = e.data;
if (action === 'start') {
interval = intervalMs;
startTime = Date.now();
expectedTime = startTime + interval;
tick();
} else if (action === 'stop') {
if (timerId) {
clearTimeout(timerId);
timerId = null;
}
}
};
function tick() {
const now = Date.now();
const drift = now - expectedTime;
self.postMessage({ type: 'tick', drift: drift });
expectedTime += interval;
const nextDelay = Math.max(0, interval - drift);
timerId = setTimeout(tick, nextDelay);
}
`;
const blob = new Blob([workerCode], { type: 'application/javascript' });
this.worker = new Worker(URL.createObjectURL(blob));
this.worker.onmessage = (e) => {
if (e.data.type === 'tick') {
this.callback(e.data.drift);
}
};
this.worker.postMessage({ action: 'start', intervalMs: this.interval });
this.isRunning = true;
}
stop() {
if (this.worker) {
this.worker.postMessage({ action: 'stop' });
this.worker.terminate();
this.worker = null;
}
this.isRunning = false;
}
}
// 使用示例
const workerTimer = new WorkerTimer((drift) => {
console.log(`执行回调,偏差: ${drift}ms`);
}, 1000);
4. 混合策略定时器
结合多种方案的优势:
javascript
class HybridTimer {
constructor(callback, interval, options = {}) {
this.callback = callback;
this.interval = interval;
this.options = {
useWorker: options.useWorker || false,
maxDrift: options.maxDrift || 50,
usePrecisionMode: options.usePrecisionMode || false,
...options
};
this.timer = null;
this.isRunning = false;
}
start() {
if (this.isRunning) return;
// 根据配置选择最适合的定时器
if (this.options.useWorker && typeof Worker !== 'undefined') {
this.timer = new WorkerTimer(this.callback, this.interval);
} else if (this.options.usePrecisionMode) {
this.timer = new HighPrecisionTimer(this.callback, this.interval);
} else {
this.timer = new AccurateTimer(this.callback, this.interval);
}
this.timer.start();
this.isRunning = true;
}
stop() {
if (this.timer) {
this.timer.stop();
this.timer = null;
}
this.isRunning = false;
}
}
5. 实时监控和动态调整
javascript
class AdaptiveTimer {
constructor(callback, interval) {
this.callback = callback;
this.interval = interval;
this.driftHistory = [];
this.maxHistorySize = 10;
this.isRunning = false;
// 性能监控
this.performanceMonitor = {
startTime: null,
executionTimes: [],
averageExecutionTime: 0
};
}
start() {
if (this.isRunning) return;
this.isRunning = true;
this.expected = performance.now() + this.interval;
this.scheduleNext();
}
scheduleNext() {
if (!this.isRunning) return;
const now = performance.now();
const drift = now - this.expected;
// 记录偏差历史
this.recordDrift(drift);
// 执行回调并监控性能
this.executeCallback();
// 动态调整下次执行时间
const adjustment = this.calculateAdjustment();
this.expected += this.interval;
const nextDelay = Math.max(0, this.interval - drift - adjustment);
setTimeout(() => this.scheduleNext(), nextDelay);
}
recordDrift(drift) {
this.driftHistory.push(drift);
if (this.driftHistory.length > this.maxHistorySize) {
this.driftHistory.shift();
}
}
calculateAdjustment() {
if (this.driftHistory.length < 3) return 0;
// 计算平均偏差趋势
const averageDrift = this.driftHistory.reduce((a, b) => a + b, 0) / this.driftHistory.length;
// 根据趋势进行预测性调整
return averageDrift * 0.5; // 保守调整
}
executeCallback() {
const startExecution = performance.now();
try {
this.callback();
} catch (error) {
console.error('Timer callback error:', error);
}
const executionTime = performance.now() - startExecution;
this.updatePerformanceMetrics(executionTime);
}
updatePerformanceMetrics(executionTime) {
this.performanceMonitor.executionTimes.push(executionTime);
if (this.performanceMonitor.executionTimes.length > 20) {
this.performanceMonitor.executionTimes.shift();
}
this.performanceMonitor.averageExecutionTime =
this.performanceMonitor.executionTimes.reduce((a, b) => a + b, 0) /
this.performanceMonitor.executionTimes.length;
}
getStats() {
return {
averageDrift: this.driftHistory.length > 0 ?
this.driftHistory.reduce((a, b) => a + b, 0) / this.driftHistory.length : 0,
maxDrift: Math.max(...this.driftHistory),
minDrift: Math.min(...this.driftHistory),
averageExecutionTime: this.performanceMonitor.averageExecutionTime
};
}
stop() {
this.isRunning = false;
}
}
6. 完整的测试和对比工具
javascript
class TimerAccuracyTest {
static async compareTimers(interval = 1000, testDuration = 10000) {
const results = {};
// 测试不同定时器的准确性
const timers = {
'setTimeout': () => new Promise(resolve => {
const drifts = [];
let expected = Date.now() + interval;
let count = 0;
const maxCount = testDuration / interval;
function tick() {
const drift = Date.now() - expected;
drifts.push(drift);
expected += interval;
count++;
if (count < maxCount) {
setTimeout(tick, interval);
} else {
resolve(drifts);
}
}
setTimeout(tick, interval);
}),
'AccurateTimer': () => new Promise(resolve => {
const drifts = [];
let count = 0;
const maxCount = testDuration / interval;
const timer = new AccurateTimer(() => {
count++;
if (count >= maxCount) {
timer.stop();
resolve(drifts);
}
}, interval);
timer.start();
})
};
for (const [name, createTimer] of Object.entries(timers)) {
console.log(`测试 ${name}...`);
const drifts = await createTimer();
results[name] = {
averageDrift: drifts.reduce((a, b) => a + b, 0) / drifts.length,
maxDrift: Math.max(...drifts),
minDrift: Math.min(...drifts),
standardDeviation: this.calculateStandardDeviation(drifts)
};
}
return results;
}
static calculateStandardDeviation(values) {
const avg = values.reduce((a, b) => a + b, 0) / values.length;
const squareDiffs = values.map(value => Math.pow(value - avg, 2));
const avgSquareDiff = squareDiffs.reduce((a, b) => a + b, 0) / values.length;
return Math.sqrt(avgSquareDiff);
}
}
// 使用测试工具
// TimerAccuracyTest.compareTimers(100, 5000).then(console.log);
方案选择建议
flowchart TD
A[需要精确计时?] --> B{精度要求}
B -->|<10ms| C[自校正定时器]
B -->|<5ms| D[HighPrecisionTimer + RAF]
B -->|<1ms| E[Web Workers方案]
C --> F[适用于大多数场景]
D --> G[适用于动画和UI更新]
E --> H[适用于后台数据处理]
style C fill:#e8f5e8
style D fill:#fff3e0
style E fill:#e1f5fe
总结
- 自校正定时器 - 最实用的通用解决方案
- 高精度定时器 - 适合需要更高精度的场景
- Web Workers - 适合后台长时间运行的定时任务
- 混合策略 - 根据环境和需求自动选择最佳方案
- 监控调整 - 动态适应运行环境的变化