??EventLoop
JS是單執行緒的,也就是,同一時間只能做一件事情,所以一旦遇到大量任務或者耗時的任務時,比如加載高清圖片,網頁就會“假死”,無法回應用戶的行為,為了防止這種阻塞,才有了同步和異步的概念,而EventLoop,即事件回圈機制,就是我們經常使用異步的原理,
同步任務
- 非耗時的任務,指的是在主執行緒上排隊執行的那些任務
- 只有前一個任務執行完畢,才能執行后一個任務
console.log('111')
console.log('222')
console.log('333')
上述就是簡單的同步任務,會按照順序輸出111,222,333
異步任務
- 耗時任務,異步任務由JS委托給宿主環境進行執行
- 當異步任務執行完之后,會通知JS主執行緒執行異步任務的回呼函式
比如說發一個網路請求,主程式需要等到接收到資料后再去做其他的事情,當異步完成后,可能程式正在做其他的事情,所以即使異步完成了也需要在一旁等待,等到程式空閑下來才有時間繼續執行,
異步任務分為宏任務和微任務,
為了防止一個函式執行時間過長阻塞后面的代碼:
- 會先將同步代碼壓入執行堆疊中,在主執行緒上依次執行;
- 將異步代碼推入任務佇列,任務佇列又分為宏任務佇列和微任務佇列;
- 因為宏任務佇列的執行時間較長,所以微任務佇列要優先于宏任務佇列,
- 如果執行堆疊中的所有同步任務執行完畢,就會讀取任務佇列,將可運行的異步任務添加到執行堆疊中,開始執行,
宏任務
每次執行堆疊執行的代碼就是一個宏任務(包括每次從事件佇列中獲取一個事件回呼并放到執行堆疊中執行)
瀏覽器為了能夠使得JS內部宏任務與DOM任務能夠有序的執行,會在一個宏任務執行結束后,在下一個宏任務執行開始前,對頁面進行重新渲染,所以宏任務執行是在DOM渲染之后,
宏任務包括:
- 異步Ajax請求
- setTimeout、setInterval
- postMessage
- 檔案操作
微任務
微任務就是在當前任務結束之后立即執行的任務,微任務在渲染之前執行,所以它的回應速度相比宏任務會更快,因為無需等渲染,
微任務包括:
- Promise.then、.catch、.finally
- process.nextTick

運行機制
- 先執行主執行緒中的同步任務
- 遇到異步宏任務,就將其放入宏任務佇列中
- 遇到異步微任務,就將其放入微任務佇列中
- 主執行緒任務執行完后,從微任務佇列中取出任務到主執行緒中,直到微任務佇列為空,在這個程序中,如果遇到微任務中又有微任務,會將其依次放入微任務佇列中
- 微任務執行完畢后,從宏任務佇列中取出任務到主執行緒中,在一個宏任務的執行程序中,如果遇到微任務,就會將其放入到微任務佇列中,
- 每一個宏任務執行完之后,都會檢查是否存在正在等待執行的微任務,如果有就會先將所有微任務執行完,再執行下一個宏任務,
根據上述的解釋,先來判斷一下這段代碼的輸出結果
setTimeout(function(){
console.log('1')
})
new Promise(function(resolve){
console.log('2')
resolve()
}).then(function(){
console.log('3')
})
console.log('4')
正確的輸出結果是:2431
執行的程序:
①先執行所有的同步任務(第5行、第10行)
②再執行微任務(第8行,promise.then是微任務)
③在執行下一個宏任務(第2行,setTimeout是宏任務)
再看一個復雜的例子
console.log('1');
setTimeout(() => {
console.log('2');
new Promise(function(resolve) {
console.log('3')
resolve()
}).then(function() {
console.log('4')
})
})
new Promise(function(resolve) {
console.log('5')
resolve()
}).then(function() {
console.log('6')
})
setTimeout(() => {
console.log('7')
new Promise(function(resolve) {
console.log('8')
resolve()
}).then(function() {
console.log('9')
})
})
輸出的結果為:156234789
需要注意的點就是執行完每個宏任務,都會把微任務佇列中的執行完,才能再執行下一個宏任務
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/458677.html
標籤:JavaScript
