setTimeout(function(){
console.log('定時器開始啦')
});
new Promise(function(resolve){
console.log('立刻執行for循環啦');
for(var i = 0; i < 10000; i++){
if(i == 99) resolve();
}
}).then(function(){
console.log('執行then函數啦');
});
console.log('代碼執行結束');
// 立刻執行for循環啦
// 代碼執行結束
// 執行then函數啦
// 定時器開始啦
// 若是沒有執行對,請往下看
複製代碼
爲何
JS
是單線程的?做爲瀏覽器腳本語言,JavaScript 的主要用途是與用戶互動,以及操做 DOM 。這決定了它只能是單線程,不然會帶來很複雜的同步問題。好比,假定 JavaScript 同時有兩個線程,一個線程在某個DOM
節點上添加內容,另外一個線程刪除了這個節點,這時瀏覽器應該以哪一個線程爲準?javascript
單線程就意味着,全部任務都須要排隊,前一個任務結束,才能執行後一個任務。若是前一個任務耗時很長,那麼後一個任務就不得不一直等待 因而乎,
JS
設計者們把全部任務分紅兩類,同步和異步前端
WebAPI
時,就會觸發異步操做,此時瀏覽器會單獨開線程去處理這些異步任務。
WebAPI
是啥?瀏覽器事件、定時器、ajax
,這些操做不會阻塞 JS 的執行,JS 會跳過當前代碼,執行後續代碼java
如何知道主線程執行執行完畢?JS引擎存在 monitoring process 進程,會持續不斷的檢查主線程執行爲空,一旦爲空,就會去 callback queue 中檢查是否有等待被調用的函數。node
console.log('1');
setTimeout(function() {
console.log('2');
}, 0);
console.log('3');
複製代碼
執行結果以下:ajax
setTimeout
) ,瀏覽器新開定時器線程處理,執行完成後把回調函數存放到回調隊列中。專業一點的說發: JS
引擎遇到異步任務後不會一直等待其返回結果,而是將這個任務掛起交給其餘瀏覽器線程處理,本身繼續執行主線程中的其餘任務。這個異步任務執行完畢後,把結果返回給回調隊列。被放入的代碼不會被當即執行。而是當主線程全部同步任務執行完畢, monitoring process 進程就會把 "回調隊列" 中的第一個回調代碼放入主線程。而後主線程執行代碼。如此反覆setTimeout
不會阻塞同步代碼,所以會首先打印3異步任務的執行優先級並不相同,它們被分爲兩類:微任務( micro task ) 和 宏任務( macro task ) 根據異步事件的類型,這些事件實際上會被派發對應的宏任務和微任務中,在當前主線程執行完畢後,promise
- 會先查看微任務中是否有事件存在,若是不存在,則再去找宏任務
- 若是存在,則會依次執行隊列中的參數,直到微任務列表爲空,讓後去宏任務中一次讀取事件到主線程中執行,如此反覆 當前主線程執行完畢後,會首先處理微任務隊列中的事件,讓後再去讀取宏任務隊列的事件。在同一次事件循環中,微任務永遠在宏任務以前執行。
script
、setTimeout
、setInterval
、UI交互事件
、I/O
process.nextTick
、Promise
、MutaionObserver
總體script自己就是一次宏任務瀏覽器
(function test() {
setTimeout(function() {console.log(4)}, 0);
new Promise(function (resolve, reject) {
console.log(1);
for( var i=0 ; i<10000 ; i++ ) {
i == 9999 && resolve();
}
console.log(2);
}).then(function() {
console.log(5);
});
console.log(3);
})()
1. setTimeout:宏任務:存入宏任務隊列
2. Promise:函數自己是同步執行的( **Promise** 只有一個參數,默認new的時候就會同步執行), `.then` 是異步,所以依次打印1、2 `.then` 存入微任務中
3. 打印3( 第一次主線程執行完畢 )
4. 執行微任務中的回調函數:5, 讓後執行宏任務中的 `setTimeout` 4
// 最終結果1,2,3,5,4
複製代碼
console.log(1)
setTimeout(() => {
console.log(2)
new Promise(resolve => {
console.log(4)
resolve()
}).then(() => {
console.log(5)
})
})
new Promise(resolve => {
console.log(7)
resolve()
}).then(() => {
console.log(8)
})
setTimeout(() => {
console.log(9)
new Promise(resolve => {
console.log(11)
resolve()
}).then(() => {
console.log(12)
})
})
第一次同步執行:1,7,8
宏任務:setTimeout
2,4,5
9,11,12
複製代碼
console.log('先執行這裏');
setTimeout(() => {
console.log('再執行啦');
}, 0);
複製代碼
- 在工做中,常常會遇到上述的代碼,含義:只要主線程執行完成,就立馬執行
setTimeout
中的回調代碼- micro-task 優先於 macro-task 執行,在瀏覽器中高優先級的代碼能夠在
promise
或setTimeout(()=>{}, 0)
中執行- 認知尚淺,只能想到這些應用場景
這些概念是中級以上前端開發必知必會內容,其實實際的落地場景不多異步
- 爲何
new Promise
第一個參數是同步執行的 ?學習Promise && 簡易實現Promisenode
中的 JS 執行機制是什麼樣子的?從一道執行題,瞭解Node中JS執行機制
附:這篇博客 也許 想表達 瀏覽器環境中JS同步和異步的執行過程 (⊙﹏⊙)b函數