使用 requestAnimationFrame 實現定時器,解決 setInterval 執行次數丟失問題

來看這樣一個場景:使用 setInterval 定時器倒計時,突然來了一個長達三秒的任務,定時器會有一次不準,兩次丟失回調,導致少兩次計算時間。

// 在控制檯上輸入下面四行
var second = 0
setInterval(function() {
  console.log(`setInterval ${++second}`, new Date().getTime())
}, 1000)

// 幾秒之後輸入下面代碼
function sleep(ms) {
  const end = new Date().getTime() + ms
  console.log('sleep start')
  while (new Date().getTime() < end) {}
  console.log('sleep end')
}
sleep(3000)

如圖所示,少兩次回調的執行。


requestAnimationFrame 實現定時器

requestAnimationFrame 傳入一個回調函數,該回調函數會在瀏覽器下一次重繪之前執行,詳情查看MDN文檔 window.requestAnimationFrame

/**
 * 設置精度定時器
 * @param {function} 回調函數
 * @param {number}   延遲時間
 * @return {number}  定時器ID
 */
function setIntervalPrecision(callback, delay) {
  // 生成並記錄定時器ID
  let obj = window.interValPrecisionObj || (window.interValPrecisionObj = { num: 0 })
  obj.num++
  obj['n' + obj.num] = true
  var intervalId = obj.num
  // 開始時間
  var startTime = +new Date()
  // 已執行次數
  var count = 0
  // 延遲時間
  delay = delay || 0


  ;(function loop() {
    // 定時器被清除,則終止
    if (!obj['n' + intervalId]) return

    // 滿足條件執行回調
    if (+new Date() > startTime + delay * (count + 1)) {
      count++
      callback(count)
    }

    requestAnimationFrame(loop)
  })()

  return intervalId
}

/**
 * 清除精度定時器
 * @param {number} 定時器ID
 */
function clearIntervalPrecision(intervalId) {
  if (window.interValPrecisionObj) {
    delete window.interValPrecisionObj['n' + intervalId]
  }
}

測試

// 在控制檯上輸入下面四行
setIntervalPrecision(function(val) {
  console.log(`setIntervalPrecision ${val}`, new Date().getTime())
}, 1000)

// 幾秒之後輸入下面代碼
function sleep(ms) {
  const end = new Date().getTime() + ms
  console.log('sleep start')
  while (new Date().getTime() < end) {}
  console.log('sleep end')
}
sleep(3000)

任務阻塞結束後,會瞬間執行阻塞期間需要執行次數的回調,雖然倒計時頁面會卡三秒(js特性),但實際剩餘秒數不會出錯。


whosmeya.com

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章