この解答例は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));  }}