js的eventloop中的宏任務和微任務理解,實現對setTimout定時器補償
寫有關宏任務和微任務的優秀博文有很多已經差不多講的很詳細,寫這邊博客並不是要做一個補充, 大略只是對自己學習理解做一個記錄補充,同時進行分享。有助於輸出推動學習。
外文鏈接:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/?utm_source=html5weekly
event loop(事件循環)
我們js運行是單線程的,所以js的執行事件就需要按照順序排列一項一項執行任務,當執行完成這次的之後就會等待下一次事件隊列執行,如此循環往復就叫 eventLoop
同步&異步任務執行順序
我們都知道js的代碼運行分爲同步和異步,同步代碼不會等待異步代碼執行完畢,而是先執行同步代碼後執行異步代碼。直接看圖:(網上素材)
解釋一下:
1.當一個一個事件循環開始時,同步事件直接放到主線程執行,異步事件放到event table並回調事件, 異步事件執行玩之後需要調用回調的時候,就到event queue中等待真正執行。
2.事件處理機制讀取主線程任務並執行完畢。
3.之後從event queue中讀取回調到主線程的事件執行
也就是說,當異步事件執行完之後回調並不是立即執行的,而是要等待主線程同步任務執行完畢
所以當我們使用setTimeout作爲定時器時,經常會不精確,因爲有可能主線程需要運算很久的話,定時器的回調就會一直等待同步代碼執行完畢
function func() {
setTimeout(()=> {
console.log("異步")
},100)
/*
一段需要大量計算的同步代碼... (耗時3s)
*/
console.log("同步")
}
//控制檯
同步
異步
這對於抽獎這種需要時間很準確的場景來說,就顯得很嚴重了,所以我們嘗試通過定時器補償的方法來提高精確度
let start = new Date().getTime(),
time = 0,
before = '0.0';
function instance()
{
time += 100;
before = Math.floor(time / 100) / 10;
if(Math.round(before) == before) { before += '.0'; }
document.title = before;
var diff = (new Date().getTime() - start) - time;
setTimeout(instance, (100 - diff));
}
setTimeout(instance, 100);
宏任務&微任務
宏任務和微任務都是異步執行的事件,但是又分執行先後。
宏任務主要包括: 執行script代碼塊,setTimeout,setInterval、setImmediate。
微任務包括: 原生Promise、process.nextTick、Object.observe、 MutationObserver
其中我們最常見的就是 setTimeout,setInterval,Promise這幾個
//代碼
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
//輸出
script start
script end
promise1
promise2
setTimeout
從上面我們可以看出執行順序是:
同步任務 => Promise回調(微任務) => setTimeout(宏任務)
但是其實真正的執行順序是宏任務先被放到event Table中註冊執行,微任務後被放到event Table中註冊執行,但是最後主線程是從event Queue先取出微服務的回調執行,宏服務的回調後執行。
如圖:(網上素材)
總來的來說了解event loop事件循環處理機制還是很有必要的,讓我們瞭解爲什麼代碼是這樣執行的,可以避免不必要的錯誤。