上一篇中@TDGarden說:又到了春暖花開、萬物復甦的季節,你們都忙着談戀愛,沒時間寫博客了。
而後在羣裏發了一張圖給我:html
好了,不瞎扯了,咱們進入正題,來聊聊Event Loop。本文算是對這幾天來學習Event Loop的總結和梳理,參考了不少大佬的文章,若有錯誤,懇請指正。前端
衆所周知,js是一種單線程語言。爲何是單線程呢?我引用一句爛大街的話:假設js同時有兩個線程,一個線程想要在某個dom節點上增長內容,另外一個線程想要刪除這個節點,這時要以哪一個爲準呢?固然,多線程有多線程的解決辦法,加鎖啊,可是這樣的話,又會引入鎖、狀態同步等問題。es6
js是瀏覽器腳本語言,主要用途是與用戶互動,操做dom,多線程會帶來很複雜的同步問題。web
好吧,那就單線程吧。可是單線程又帶來了單線程的問題,只有一個線程啊,任務要排隊執行,若是前一個任務執行時間很長(ajax請求後臺數據),後面的任務就都得等着。面試
Event Loop就出現了,來背單線程的鍋。ajax
往下看以前你應該知道棧、隊列、同步任務、異步任務、執行棧這些基本概念。api
關於執行棧有一篇很詳細的文章推薦:JavaScript深刻之執行上下文棧promise
請看下圖: 瀏覽器
js在執行代碼時,代碼首先進入執行棧,代碼中可能包含一些同步任務和異步任務。bash
同步任務當即執行,執行完出棧,over。
異步任務也就是常見的ajax
請求、setTimeout
等,代碼調用到這些api的時候,WebAPIs來處理這些問題,執行棧繼續執行。
異步任務有了運行結果時,(當ajax
請求結果返回時),WebAPIs把對應的回調函數放到任務隊列。
執行棧爲空時來讀取任務隊列中的第一個函數,壓入執行棧。
步驟5不斷重複,執行棧爲空時,系統就去任務隊列中拿第一個函數壓入棧繼續執行。這個過程不斷重複,這就是事件循環(Event Loop)。
來看一個簡單的demo。
console.log(1);
setTimeout(() => {
console.log(2);
}, 2000);
console.log(3);
複製代碼
console.log(1)
同步任務,輸出1setTimeout
異步任務,交給webapis去處理,2s後,console.log(2)
進入任務隊列console.log(3)
同步任務,輸出3console,log(2)
,輸出2說到這兒固然還沒完。相信你確定見過process.nextTick
、promise
吧,這時候執行順序會有點兒複雜,往下看。
微任務、宏任務與Event-Loop用了很通俗的例子講了宏任務和微任務的區別,我這裏就不囉嗦了。若是你不想了解也不要緊,由於常見的宏任務、微任務就那幾種,記住就能夠了。
script(總體代碼)
、setTimeout
、setInterval
、I/O
、setImmedidate
process.nextTick
、MutationObserver
、Promise.then catch finally
process.nextTick
和setImmidate
是隻支持Node環境的。
還有,process.nextTick
是有一個插隊操做的,就是說他進入微任務隊列時,會插到除了process.nextTick
其餘的微任務前面。
因此,咱們上面提到的任務隊列,是包括一個宏任務隊列和一個微任務隊列的。每次執行棧爲空的時候,系統會優先處理微任務隊列,處理完微任務隊列裏的全部任務,再去處理宏任務。
前面叨叨了那麼多,下面作兩道題試試水吧。
new Promise(resolve => {
resolve(1);
Promise.resolve().then(() => console.log(2));
console.log(4);
}).then(t => console.log(t));
console.log(3);
複製代碼
hahahhhhh我搬出了阮老師的題。
new Promise
執行,resolve(1)
表示建立的promise對象的狀態變爲resolvedPromise.resolve()
至關於建立了一個promise對象,then裏面的匿名回調函數進入微任務隊列,此時的微任務隊列是[() => console.log(2)]
new Promise
的then函數裏面的匿名回調進入微任務隊列, 此時的微任務隊列是[() => console.log(2), t => console.log(t)]
因此,最後輸出的順序是4 3 2 1。
emmmmmmm若是你不懂,那我以爲你能夠先去複習一下promise。
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
process.nextTick(() => {
console.log('nextTick');
})
console.log('script end');
複製代碼
這是一道爛大街的面試題。。我不信你沒見過。話很少說,咱們來分析emmmmm我建議你本身先作一下,再往下看。
看到async/await
沒必要緊張,語法糖而已。async
表示函數裏有異步操做,await
以前的代碼該怎麼執行怎麼執行,await
右側表達式照常執行,後面的代碼被阻塞掉,等待await
的返回。返回是非promise
對象時,執行後面的代碼;返回promise
對象時,等promise
對象resolved
時再執行。
因此能夠理解成後面的代碼放到了promise.then
裏面。
script start
setTimeout
裏面的匿名回調函數丟進宏任務隊列,簡記爲['setTimeout']
(請記得丟進任務隊列裏的是回調函數,函數!)async1 start
async2
async1 end
代碼被丟進微任務隊列,此時的微任務隊列爲['async1 end']
promise1
promise
對象狀態變爲resolved
promise.then
裏的匿名函數進入微任務隊列,此時的微任務隊列爲['async1 end', 'promise2']
nextTick
插隊到微任務隊列對首,['nextTick', 'async1 end', 'promise2']
script end
nextTick
async1 end
promise2
setTimeout
若是看完本文你仍是沒太懂,那我建議你能夠多看幾篇文章,一個燒餅吃不飽,十個就差很少了。
涉及到promise
、async/await
、Node
和瀏覽器環境下事件循環的區別等問題本文沒有細講,可是這些知識會幫你更好地掌握Event Loop。
看完promise
能夠作一下題試試水:Eventloop不可怕,可怕的是趕上Promise。
Author:小夭yao愛吃糖糖糖