爲什麼不推薦使用 setInterval

解釋原因之前需要先簡單介紹一下 js 的執行原理:js引擎是單線程的,主要分爲主線程和事件隊列,同步操作是在主線程上執行,而異步操作的函數會先放在事件隊列當中,等到js主線程空閒了,纔會去事件隊列取出放到主線程執行。定時器是屬於異步事件,參數裏面設置的時間,並不是延遲多少秒去執行回調函數,這個時間代表的是延遲多少秒,把回調函數放到異步隊列,等待主線程空閒再被執行。

如果按照上面的說法,假如同步代碼耗時較長就會存在執行多次的問題,舉個例子,假如設置一個 100ms 的定時器,定時器中代碼需要執行1000ms,那麼事件隊列中就會添加10次定時器的函數(實際小於10次),這樣1s之後會就執行10次代碼,而不是我們想要的每隔 100ms 執行一次,對此js引擎解決方法是,當使用setInterval時,僅當沒有該定時器的任何其他代碼實例時,纔將定時器代碼添加到隊列中。這確保了定時器代碼加入到隊列中的最小時間間隔爲指定間隔。什麼意思呢,看如下代碼:

console.time('耗時')
var timer = setInterval(() => {
  for (let i = 0; i < 1100000000; i++) { // 900ms左右
  }
  console.log(0)
}, 100);

setTimeout(() => {
  console.log('1s了')
  clearInterval(timer)
  console.timeEnd('耗時') // 耗時: 1955.52490234375ms(打印結果不固定)
}, 1000);

看上去這裏應該打印10個0纔對,其實只會打印2個0,原因就是如果當事件隊列當中,已經存在了定時器的回調函數,即使已經到了規定的間隔時間,也不會再把這個時間點的定時器回調函數放到事件隊列當中,定時器依舊運行。當下一個約定時間又到了,如果事件隊列當中依然存在定時器的回調函數,這個時間點的定時器回調函數也不會放進事件隊列…

這樣就會導致一些間隔被跳過了。如果需要每個定時器的回調函數都被執行到,這裏就不能滿足需求了。

還有一個常用的場景是使用 setInterval 進行ajax請求,如果某一次請求時間過長,就導致下一次甚至多次的請求被忽略掉,這顯然是不能接受的。

迭代setTimeout

爲了避免setInterval()定時器的問題,可以使用鏈式setTimeout()調用

setTimeout(function fn(){
    setTimeout(fn,interval);
},interval);

這個模式鏈式調用了 setTimeout ,每次函數執行的時候都會創建一個新的定時器。第二個setTimeout()調用當前執行的函數,併爲其設置另外一個定時器。這樣做的好處是,在前一個定時器代碼執行完之前,不會向隊列插入新的定時器代碼,確保不會有任何缺失的間隔。而且,它可以保證在下一次定時器代碼執行之前,至少要等待指定的間隔,避免了連續的運行。因此 setTimeout 在某程度上比 setInterval 穩定。但無論是 setTimeout 還是 setInterval 都無法解決精準定時的問題。

參考:

https://blog.csdn.net/chiuwingyan/article/details/80322289

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