setInterval和setTimeout是前端開發中經常會用到的定時器,下面對其原理進行一下分析。
javascript是單線程的,一個javascript運行時包含了一個待處理的消息隊列,每條消息都代表要運行的函數。當調用棧為空時,會從隊列里取出條消息進行處理。
setTimeout方法包含兩個參數,第一個參數為一個函數或者一個會作為eval()方法參數的js代碼字符串,第二個參數為以毫秒為單位的時間。該方法的實際作用即:在一定時間之后,把一個函數加入消息隊列末尾。如果這個時間點消息隊列中還存在其他消息,那么該函數會在排在他之前的消息都執行完之后再開始執行。所以,這個時間會小於等於該函數開始運行的時間。
如上圖所示,函數a,b,c執行時間均為300ms,a在執行setTimeout時開始執行。在400ms之后,c加入了消息隊列,但此時調用棧中函數b仍在執行,c只能等b執行完之后才能開始執行,所以c從加入隊列之后第600ms才開始執行。
1 setTimeout(function() { 2 console.timeEnd('setTimeout實際等待時間:'); 3 },1000) 4 console.time('setTimeout實際等待時間:'); 5 6 console.time("執行循環時間:"); 7 for (let i = 0; i < 10000; i++) { 8 console.log(i); 9 } 10 console.timeEnd("執行循環時間:");
以上代碼,在開始時在消息隊列里加入一個函數,並開始計時。該函數被放在了隊列的末尾,排在循環之后。於是循環會先執行,在循環和console語句執行完之后,setTimeout中的函數才會被執行。把該段代碼該段代碼在chrome中執行,最后的結果如下圖:
可見,setTimeout中的函數是在循環及console函數執行完之后才開始執行。
我們有時會在代碼中看到如下代碼:
setTimeout(func, 0)
現在我們就可以理解該函數的意思了。這個函數的作用就是把func函數放到隊列的末尾,在所有函數都執行完之后再執行。
1 setTimeout(function() { 2 console.log('setTimeout') 3 }) 4 console.log('test1'); 5 console.log('test2'); 6 7 // test1 8 // test2 9 // setTimeout
setInterval函數的原理與setInterval是相同的,就不再贅述。