我們都知道JavaScript是單線程的,也就是說同一時間只能幹一件事。這是因爲JavaScript主要是用來操作DOM
的,如果變成多線程,瀏覽器就懵逼了,不知道該聽誰的了。但是雖然js是單線程,但是完全可以模擬多線程,靠的就是Event Loop
。
我們都知道js中的代碼分 同步
和 異步
,所謂的 異步
其實就是不會阻塞我們的主線程,等待主線程的代碼執行完畢纔會執行。callback setTimeout setInterval Promise ...
這些都是都是我們耳熟能詳的 異步
代碼
如圖所示,js中的內存分爲 堆內存(heap)
和 棧內存(stack)
, 堆內存
中存的是我們聲明的object
類型的數據,棧內存
中存的是 基本數據類型
以及 函數執行時
的運行空間。我們的 同步
代碼就放在 執行棧
中,那異步代碼呢?瀏覽器會將 dom事件 ajax setTimeout
等異步代碼放到隊列中,等待執行棧
中的代碼都執行完畢,纔會執行隊列中的代碼,是不是有點像發佈訂閱模式。
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
console.log(3);
複製代碼
根據之前說的,setTimeout 會被放到隊列中,等待執行棧中的代碼執行完畢纔會執行,所以會輸出1, 3, 2
但是異步
代碼也是有區別的:
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
Promise.resolve().then(() => {
console.log(3)
})
複製代碼
輸出的永遠是1, 3, 2
, 也就是說 promise
在 setTimeout
之前執行了。這是因爲 異步任務
分爲 微任務(microtask)
和 宏任務(task)
,執行的順序是 執行棧中的代碼 => 微任務 => 宏任務
。
執行棧
執行棧
中的代碼永遠最先執行
微任務(microtask): promise MutationObserver...
- 當
執行棧
中的代碼執行完畢,會在執行宏任務隊列
之前先看看微任務隊列
中有沒有任務,如果有會先將微任務隊列
中的任務清空纔會去執行宏任務隊列
宏任務(task): setTimeout setInterval setImmediate(IE專用) messageChannel...
- 等待
執行棧
和微任務隊列
都執行完畢纔會執行,並且在執行完每一個宏任務
之後,會去看看微任務隊列
有沒有新添加的任務,如果有,會先將微任務
隊列中的任務清空,纔會繼續執行下一個宏任務
setTimeout(() => {
console.log('timeout1')
Promise.resolve().then(() => {
console.log('promise1')
})
Promise.resolve().then(() => {
console.log('promise2')
})
}, 100)
setTimeout(() => {
console.log('timeout2')
Promise.resolve().then(() => {
console.log('promise3')
})
}, 200)
複製代碼
- 先將兩個
setTimeout
塞到宏任務隊列中 - 當第一個
setTimeout1
時間到了執行的時候,首先打印timeout1,然後在微任務隊列中塞入promise1
和promise2
- 當第一個
setTimeout1
執行完畢後,會去微任務隊列檢查是不是空的,他發現了有兩個promise
,會把兩個promise
按順序執行完再去執行下一個宏任務 - 兩個
promise
執行完畢後會微任務隊列中沒有任務了,會去宏任務中執行下一個任務setTimeout2
- 當
setTimeout2
執行的時候,先打印一個timeout2,然後又在微任務隊列中塞了一個promise2
- 當
setTimeout2
執行完畢後會去微任務隊列檢查,發現有一個promise2,會將promise2
執行 - 會依次打印
timeout1 promise1 promise2 timeout2 promise3
作者:asdff
鏈接:https://juejin.im/post/5b63b4cb6fb9a04fb4017f5a
來源:掘金
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。