この解答例はChatGPTによって生成されています,正しいかは自己判断で。 皆の投稿からも解答例が見つかるかもしれませんよ。
BFE.devでのAI解答例 - JavaScript コーディング問題
84. fake timer (setInterval)を実装する
class FakeTimer { install() { const originSetInterval = window.setInterval; const originClearInterval = window.clearInterval; const originDateNow = Date.now; let currentTime = 0; let intervalIdMap = new Map(); window.setInterval = function (fn, delay) { let id = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); const intervalFunc = () => { fn(); intervalIdMap.set(id, originSetInterval(intervalFunc, delay)); }; intervalIdMap.set(id, originSetInterval(intervalFunc, delay)); return id; }; window.clearInterval = function (intervalId) { const interval = intervalIdMap.get(intervalId); if (interval) { originClearInterval(interval); intervalIdMap.delete(intervalId); } }; Date.now = function () { return currentTime; }; let previousTickTime = Date.now(); this.tick = function (duration = 0) { const ticksCount = Math.ceil(duration / 10); const newCurrentTime = currentTime + duration; for (let i = 0; i < ticksCount; i++) { previousTickTime = currentTime; currentTime += 10; intervalIdMap.forEach((interval, id) => { if (currentTime - previousTickTime === interval.delay) { interval.func(); } }); } }; } uninstall() { window.setInterval = originSetInterval; window.clearInterval = originClearInterval; Date.now = originDateNow; } tick() { this.tick(10); }}
補足
setInterval
関数をトラッキングするためにintervalIdMap
という Map を定義して、setInterval
で生成した id をキー、interval id と interval function のオブジェクトを値として保存するようにしています。setInterval
をそれぞれのdelay
毎に実行し、intervalIdMap
を使ってclearInterval
を呼び出すには、以下のようにします。- id に対応する interval object を取得
- 値を実行して、次の実行をスケジューリング
currentTime
の変数は常に時間を格納し、Date.now()
への置き換え関数のために使用されています。tick()
関数は指定されたduration
の時間だけイベントループを進めています。tick()
メソッド内のループでは、intervalIdMap
内のすべてのinterval object
をチェックして、currentTime - previousTickTime
タイムアウトした場合に、オブジェクトの機能を実行しています。random
で生成されたid
がシステム上で一意であることを保証するために、setInterval
によって生成される整数の範囲を最大整数に制限しました。
let id = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
テストの例
const fakeTimer = new FakeTimer();fakeTimer.install();const logs = [];const log = () => { logs.push(Date.now());};let count = 0;const id = setInterval(() => { if (count > 1) { clearInterval(id); } else { log(); } count += 1;}, 100);// log 'A' at every 100, stop at 200fakeTimer.tick(200);fakeTimer.uninstall();expect(logs).toEqual([100, 200]);// Test for setInterval and clearIntervalconst intervals = [];const intervalsMap = new Map();const callback = () => { console.log('Callback');};const createInterval = (cb, delay) => { const id = setInterval(cb, delay); intervals.push(id); intervalsMap.set(id, { cb, delay });};createInterval(callback, 100);createInterval(callback, 1200);fakeTimer.tick(2600);intervals.forEach((intervalId) => clearInterval(intervalId));const intervalsStats = [ { intervalFn: () => console.log('Callback 1'), delay: 100, calls: 26 }, { intervalFn: () => console.log('Callback 2'), delay: 1200, calls: 2 }];let callStats = intervalsStats.map((v) => ({ ...v, calls: 0 }));for (let i = 0; i < callStats.length; i++) { let intervalInvocations = intervalsMap.get(intervals[i]); let intervalFn = intervalInvocations['cb']; let delay = intervalInvocations['delay']; for (let j = 0; j < callStats[i]['calls']; j++) { expect(logs.shift()).toBe(j * delay + (i === 0 ? 100 : 0)); }}