之因此寫這篇文章是由於上週工做中使用setInterval輪詢請求接口時遇到了一些問題,若是哪裏理解的不對請你們多多指教~javascript
let timer1 = setTimeout(() => {
}, 1000)
console.log(timer1) // 1
let timer2 = setInterval(() => {
},1000)
console.log(timer2) // 2
複製代碼
clearTimeout([定時器的排隊序號])java
clearInterval([定時器的排隊序號])ajax
let timer = setTimeout(() => {
// 定時器即便清除了,其返回值也不會清除,以後設置定時器的返回值也會在其返回值的基礎上繼續向後排,
// 相似於銀行的排隊領號,即便1號的業務辦理完了,後面的人還是從2號開始繼續領號,而不是從1開始。
clearTimeout(timer)
}, 1000)
複製代碼
注意: 定時器須要手動清除,而且clearTimeout和clearInterval均可以清除setTimeout或setInterval,但並不建議這樣作,容易形成混淆。
promise
let obj = {
fn() {
console.log(this) // obj
// 示例1
let timer1 = setTimeout(function() {
console.log('我是timer1的this指向:', this) // Window
}, 1000)
// 示例2 (讓定時器函數中的this是obj:使用變量保存的方式)
let _this = this
let timer2 = setTimeout(function() {
console.log('我是timer2的this指向:', _this) // obj
}, 1000)
// 示例3 (讓定時器函數中的this是obj:使用bind方法改變this指針)
let timer3 = setTimeout(
function() {
console.log('我是timer3的this指向:', this) // obj
}.bind(this),
1000
)
// 示例4 (讓定時器函數中的this是obj:使用箭頭函數,箭頭函數中的this繼承宿主環境(上級做用域中的this))
let timer4 = setTimeout(() => {
console.log('我是timer4的this指向:', this) // obj
}, 1000)
}
}
obj.fn()
複製代碼
涉及到的知識點:JS事件循環機制EVENTLOOP瀏覽器
注意:異步任務之間並不相同,他們的執行優先級有區別。不一樣的異步任務會被分爲兩類:微任務(micro task)和宏任務(macro task)
服務器
使用定時器的時候,千萬不要太相信預期,延遲的時間嚴格來講老是大於xxx毫秒的,至於大多少就要看當時執行的狀況了。即便設置爲0也不會立刻執行,HTM5規範定最小延遲時間不能小於4ms,不一樣瀏覽器的實現不同,好比,Chrome能夠設置1ms,IE11/Edge是4ms。網絡
setTimeout註冊的函數fn會交給瀏覽器的定時器模塊來管理,延遲時間到了就將fn加入主進程執行隊列,若是隊列前面還有沒有執行完的代碼,則又須要花一點時間等待才能執行到fn,因此實際的延遲時間會比設置的長。如在fn以前正好有一個超級大循環,那延遲時間就不是一丁點了。異步
(function testSetTimeout() {
console.time('timer');
const timer = setTimeout(() => {
console.timeEnd('timer');
}, 10);
for(let i = 0; i < 100000000; i++) {}
})();
// timer: 59.364990234375ms 遠遠不止10ms
複製代碼
setInterval有個討厭的習慣,即對本身調用的代碼是否報錯這件事不聞不問,若是setInterval執行的代碼因爲某種緣由出了錯,它還會持續不斷(無論不顧)地調用該代碼。函數
function a() {
try {
cnosole.log('單詞拼寫錯誤,應該是console')
} catch (e) {
console.log('錯誤了')
}
}
setInterval(a, 1000)
// 8VM69:5 錯誤了 (控制檯每間隔一秒就會輸出一個錯誤了)
複製代碼
假設你每隔一段時間就經過Ajax輪詢一次服務器,看看有沒有新數據。而因爲某些緣由(服務器過載、臨時斷網、流量劇增、用戶帶寬受限,等等,你的請求要花的時間遠比你想象的要長。但setInterval不在意。它仍然會按定時持續不斷地觸發請求,最終你的客戶端網絡隊列會塞滿Ajax調用。ui
例子:下面代碼並非上一次fn執行完了以後再過100ms纔開始執行下一次fn。 事實上,setInterval並無論上一次fn的執行結果,而是每隔100ms就將fn放入異步隊列,而兩次fn之間具體間隔多久就不必定了,跟setTimeout實際延遲時間相似,和JS執行狀況有關。
(function testSetInterval() {
let i = 0;
const start = Date.now();
const timer = setInterval(() => {
i++;
i === 2 && clearInterval(timer);
console.log(`第${i}次開始`, Date.now() - start);
for(let i = 0; i < 900000000; i++) {}
console.log(`第${i}次結束`, Date.now() - start);
}, 100);
})();
VM232:7 第1次開始 104
VM232:9 第1次結束 603
VM232:7 第2次開始 605
VM232:9 第2次結束 1106
複製代碼
雖然每次fn執行時間都很長,但下一次並非等上一次執行完了再過100ms纔開始執行的,實際上早就已經等在隊列裏了。 在fn被阻塞的時候,setInterval仍然在組織未來對回調函數的調用。 所以,當第一次fn函數調用結束時,已經有6次函數調用在等待執行。
最簡單也是最容易控制的方案,是在回調函數內部使用setTimeout函數。
function foo(){
setTimeout(foo, 100);
}
foo();
複製代碼