以下为ChatGPT自动生成的解答,正确与否请自行判断。 也许你也可以从大家的发帖中找到想要的答案!
BFE.devAI解答 - JavaScript 代码题
36. 实现一个 fake timer(setTimeout)
一个可能的实现如下:
class FakeTimer { constructor() { this.timers = new Map(); // 存储 timer 的 id 和 callback this.currentTime = 0; // 初始时间戳为 0 this.originalDateNow = Date.now.bind(Date); // 保存原始的 Date.now 方法 } install() { const self = this; const fakeDateNow = () => self.currentTime; // 实现一个 fake 的 Date.now 方法 Date.now = fakeDateNow; const fakeSetTimeout = (fn, delay, ...args) => { const id = Symbol(); // 生成一个唯一的id const callback = () => { fn(...args); // 执行回调 self.timers.delete(id); // 删除设置的timer self.tick(); // 继续往下执行,直到没有timer或到达下一个timer的时间 }; self.timers.set(id, callback); // 保存id和callback return id; }; window.setTimeout = fakeSetTimeout; // 在全局覆盖 setTimeout const fakeClearTimeout = id => { self.timers.delete(id); }; window.clearTimeout = fakeClearTimeout; // 在全局覆盖 clearTimeout } uninstall() { Date.now = this.originalDateNow; // 恢复原始的 Date.now 方法 window.setTimeout = undefined; // 恢复原始的 setTimeout 方法 window.clearTimeout = undefined; // 恢复原始的 clearTimeout 方法 this.timers.clear(); } tick() { while (this.timers.size > 0) { const nextTime = Math.min(...this.timers.values().map(callback => { return callback._runAt || Infinity; // 获取最近的 timer 执行时间 })); this.currentTime = nextTime; // 跳转到下一个最近的时间 for (const [id, callback] of this.timers.entries()) { if (callback._runAt === nextTime) { callback(); // 执行回调 } } } }}
实现思路:
我们首先需要一个 timers
map,用于存储当前设置的 timer,并且唯一标识它们的 id 与其回调函数。然后我们需要一个 currentTime
变量,用于记录当前时间戳,初始值为 0。我们还需要一个 Date.now()
的 fake 方法,用于提供新的时间戳。
在 install()
方法中,我们覆盖了原始的 Date.now()
方法和 setTimeout()
方法,并且通过 Symbol 生成了唯一的 id,并且将 id 和 callback 存储在 timers map 中。覆盖函数实现时,我们额外传递了一个 ...args,这是为了能够支持在 setTimeout()
中传入参数。
在 tick()
方法中,我们循环执行 timers map 中所有的回调函数,只要它们的 _runAt 属性等于 current time(即从当前时间到下一个最近的 timer 的时间内所有设置的 timer)或者它们没有设置 _runAt(即第一次执行)。
最后在 uninstall()
方法中,我们恢复了原始的 Date.now()
方法和 setTimeout()
方法,并且清空 timers map。
完整的测试:
const fakeTimer = new FakeTimer();fakeTimer.install();const logs = [];const log = (arg) => { logs.push([Date.now(), arg]);};setTimeout(() => log('A'), 100); // 在时间戳 100 上执行 callback Aconst b = setTimeout(() => log('B'), 110);clearTimeout(b); // 直接删除了 bsetTimeout(() => log('C'), 200); // 在时间戳 200 上执行 callback CfakeTimer.tick(); // 执行所有设置的 timerfakeTimer.uninstall();expect(logs).toEqual([[100, 'A'], [200, 'C']]);
其中,我们通过 fakeTimer.tick()
执行所有设置的 timer。由于我们的实现中没有真正的 timer,因此所有的 timer 都将在同一时间点被执行,如果要模拟真实世界,我们可能需要在 fakeSetTimeout()
方法中模拟一个计时器(比如使用 setInterval),然后在 fakeClearTimeout()
方法中清空这个计时器。