代碼執行順序:默認先執行主棧中的代碼,主棧執行完成後清空微任務對列,微任務對列清空後,取出第一個宏任務到主棧中執行,執行完成後再去檢查微任務對列是否有未執行任務,若是有清空,若沒有取下一個宏任務到主棧中執行,執行完成後再去檢查微任務對列是否有未執行任務,若是有清空...造成一個事件環javascript
是一種遵循後進先出(last in first out LIFO)原則的有序集合;在棧裏,新元素都靠近棧頂,舊元素都靠近棧底(現實中棧的例子:一摞書或者堆放在一塊兒的盤子)html
是一種遵循先進先出(first in first out FIFO)原則的一組有序的項;在隊列中,最新添加的元素必須排在隊列的末尾(現實中隊列的例子:排隊過安檢) java
setTimeout
setImmediate
(只有IE
支持)
setTimeout
快messageChannel
web worker
中可用MessagePort
屬性發送數據
白話說明:咱們小的時候都玩過土電話,咱們能夠新的消息通道想象成咱們的土電話,把兩個
MessagePort
屬性想像成圖中的小熊(port2
)和小兔(port1
),當小兔經過土電話向小熊喊話(port1
向另個port2
發送數據),聲音的傳遞過程(數據傳遞的過程就是)就是異步的。node
<html>
<body>
<div id="app"></div>
<script> console.log('start') // 這裏咱們爲了測試他是異步的,咱們採用port1先發送數據,port2後監聽 let channel = new MessageChannel(); let port1 = channel.port1; let port2 = channel.port2; port2.postMessage('你好') setTimeout(()=>{ console.log('setTimeout'); },0) port1.onmessage = function (e) { console.log(e); } console.log('end') </script>
</body>
</html>
複製代碼
Promise.resolve.then
MutationObserver
DOM3 Events
規範的一部分DOM
樹所作更改的能力<html>
<body>
<div id = "app"></div>
<script> /* 實現 等待dom更新後 再取dom中的數據 */ setTimeout(()=>{ console.log('timeout'); },0) // 1.建立一個MutationObserver的實例並傳入一個異步的callback let mutationObserver = new MutationObserver(()=>{ console.log(app.children.length); // 異步執行 }) // 2.觀察app的childList的變化 mutationObserver.observe(app,{childList:true}) // 3.經過腳本動態向app中插入30個p for (let i = 0; i < 10; i++) { app.appendChild(document.createElement('p')); } for (let i = 0; i < 10; i++) { app.appendChild(document.createElement('p')); } for (let i = 0; i < 10; i++) { app.appendChild(document.createElement('p')); } </script>
</body>
</html>
複製代碼
console.log('start')
setTimeout(()=>{
console.log(1);
Promise.resolve().then(()=>{
console.log(2)
})
},0);
Promise.resolve().then(data=>{
console.log(3);
setTimeout(()=>{
console.log(4);
})
})
console.log('end');
// start end 3 1 2 4
複製代碼
async function async1() {
console.log('async1Start');
await async2();
console.log('async1End');
}
async function async2() {
console.log('async2');
}
console.log('scriptStart');
setTimeout(function () {
console.log('setTimeout');
}, 0)
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
console.log('scriptEnd');
//scriptStart async1Start async2 promise1 scriptEnd async1End promise2 setTimeout
複製代碼
let guYan = async () => {
console.log(await new Promise((resolve, reject) => {
console.log('guYan');
}))
}
guYan()
複製代碼
注:前面的兩道題的分析中沒加入宏、微任務事件池的概念(就是在進入隊列以前須要先通過事件池的排列)web
node
中在主執行棧執行完畢和切換隊列的時候清空微任務node
中和瀏覽器同樣在主執行棧執行完畢和一個宏任務執行完畢後清空微任務// 咱們排除掉node內部本身調用的循環部分,其實須要咱們掌握的部分並很少,我總結爲三部分
// 1.timer階段主要表明setTimeout
// 2.poll 輪循階段 主要是i/o的讀寫(fs模塊的一些操做等)
// 3.check階段主要表明setImmediate
┌───────────────────────────┐
┌─>│ 1.timers │──┐
│ └─────────────┬─────────────┘ │ ┌───────────────┐ |
│ ┌─────────────┴─────────────┐ │ │ incoming: │ | 執
│ │ 2.poll │<─────┤ connections, │ | 行
│ └─────────────┬─────────────┘ │ │ data, etc. │ | 順
│ ┌─────────────┴─────────────┐ │ └───────────────┘ | 序
└──│ 3.check │<─┘ ↓
└───────────────────────────┘
/* *執行順序分析: * 1.從timer階段開始檢查是否有可執行的若沒有向下執行 * 2.在poll階段執行完畢後檢查timer階段是否待執行的,如有準備跳回timer階段執行; * 可是在這以前須要檢查check階段是否有待執行的,如有先跳到check階段執行,再跳到timer階段執行,若沒有可直接跳回timer階段執行; * 3.無條件按照順序執行,沒有可跳過 */
複製代碼
setTimeout
setImmediate
setInterval
readFile
文件讀寫系列promise.then
process.nextTick
setTimeout(()=>{
console.log('setTimeout');
},0)
setImmediate(()=>{
console.log('setImmediate');
})
複製代碼
node
環境中執行你會發現兩種結果
setTimeout
再輸出setImmediate
,這種狀況我不作說明哈就是正常狀況setImmediate
再輸出setTimeout
,這種狀況的緣由是:當咱們程序運行的時候咱們知道setTimeout
的第二個參數表明時間雖然咱們寫成0
可是最小時間並非0
,大約是4
。因此當執行到timer
階段的時候setTimeout
的執行時間還沒到,直接進行到poll
階段等待,等待一段時候後發現timer
階段的setTimeout
能夠執行,準備回到timer
階段執行,可是回去以前須要檢查check
階段,發現有setImmediate
須要執行let fs = require('fs');
fs.readFile(__filename,'utf-8',(err,data)=>{
setTimeout(()=>{
console.log('setTimeout')
},0)
setImmediate(()=>{
console.log('setImmediate')
})
)
複製代碼
timer
階段沒有可執行的,直接進入poll
階段,在poll
階段分別給timer
階段和check
階段增長一個待執行函數後開始等待setTimeout
的執行,當setTimeout
可執行後,準備從poll
階段跳到timer
以前檢查check
階段有可執行的setImmediate
因此永遠是先輸出setImmediate
後輸出setTimeout
js
中一個進程只有一個主線程
js
線程和UI
線程是互斥的js
代碼的加載速度defer
js
腳本的加載並行進行(即實現了異步加載js
腳本),這樣頁面加載會變快<script src="index.js" defer></script>
複製代碼
async
(h5屬性)
js
腳本無依賴關係<script src="index.js" async></script>
複製代碼
preload
(會先加載資源)prefetch
(默認不會立刻加載,等待瀏覽器空閒時偷偷加載)