EventLoop
作為一個前端er阀坏,必須知道的技能之一~由于我之前時候做Android開發(fā)的,特此記錄下笆檀。
js是單線程的忌堂,那如何協(xié)調(diào)事件(event),用戶交互(user interaction)酗洒,腳本(script)士修,渲染(rendering),網(wǎng)絡(luò)(networking)等樱衷,用戶代理(user agent)
等等呢~此時就需要用到EventLoop——事件循環(huán)棋嘲。
JS有個任務隊列的概念:
macrotask(宏任務):script(整體代碼), XHR回調(diào)、事件回調(diào)(鼠標鍵盤事件)setTimeout, setInterval, setImmediate(node獨有), I/O, UI rendering
microtask(微任務):process.nextTick(node獨有), Promises.then, Object.observe(廢棄), MutationObserver
new Promise 屬于主線程(外層宏) 但是Promises.then 是微任務>毓稹沸移!
詳細步驟:
1.選擇當前要執(zhí)行的宏任務隊列,選擇一個最先進入任務隊列的宏任務,如果沒有宏任務可以選擇雹锣,則會跳轉(zhuǎn)至microtask的執(zhí)行步驟网沾。
2.將事件循環(huán)的當前運行宏任務設(shè)置為已選擇的宏任務。
3.運行宏任務蕊爵。
4.將事件循環(huán)的當前運行任務設(shè)置為null辉哥。
5.將運行完的宏任務從宏任務隊列中移除。
6.microtasks步驟:進入microtask檢查點攒射。
7.更新界面渲染醋旦。
8.返回第一步。
只要主線程空了会放,就會讀取任務列隊饲齐,這就是 js 的運行機制,也被稱為 event loop(事件循環(huán))鸦概。
需要注意的一點是:在同一個上下文中箩张,總的執(zhí)行順序為同步代碼—>microTask—>macroTask
下面看代碼:
console.log("start")
setTimeout(()=>{
console.log('timer1')
new Promise(function(resolve){
console.log(" promise start ")
resolve();
}).then(function() {
console.log('promise1')
})
}, 0)
setTimeout(()=>{
console.log('timer2')
Promise.resolve().then(function() {
console.log('promise2')
})
}, 0)
console.log("end")
瀏覽器輸出:
start
end
time1
promise start
promise1
time2
promise2
首先先執(zhí)行同步任務,先打印start,
執(zhí)行到第一個setTimeout屬于宏任務 所以掛起放到宏任務窗市,遇到第二個setTimeout也是掛起放到宏任務,
接著打印end先慷,同步任務執(zhí)行完成->JS執(zhí)行棧為空->去查找微任務:微任務為空,接著查找宏任務咨察,
執(zhí)行第一個宏任務setTimeout 打印timer1论熙,接著執(zhí)行newPromise,輸出 promise start 摄狱,再接著Promise.then屬于微任務脓诡,掛起放到微任務中。
宏任務執(zhí)行完成->JS執(zhí)行棧為空>去查找微任務:微任務里有任務媒役,一次執(zhí)行完祝谚,輸出promise1。
后面接著循環(huán)去查找宏任務~酣衷。
換一種方式:
console.log("start")
setTimeout(()=>{
console.log('timer1')
}, 0)
new Promise(function(resolve){
console.log(" promise start ") //主線程(外層宏)
resolve();
}).then(function() {
console.log('promise1')交惯;//這里是微任務
})
setTimeout(()=>{
console.log('timer2')
Promise.resolve().then(function() {
console.log('promise2')
})
}, 0)
console.log("end")
瀏覽器輸出:
start
promise start
end
promise1
timer1
time2
promise2
console.log('1');
setTimeout(function() {
console.log('3');
new Promise(function(resolve) {
console.log('3.1');
resolve();
}).then(function() {
console.log('4')
})
})
new Promise(function(resolve) {
console.log('1.1');
resolve();
}).then(function() {
console.log('2')
})
setTimeout(function() {
console.log('5');
new Promise(function(resolve) {
console.log('5.1');
resolve();
}).then(function() {
console.log('6')
})
})
瀏覽器輸出:
1
1.1
2
3
3.1
4
5
5.1
6
async/await
如果遇到async的代碼 await后面的程序就掛起 類似于微任務 等到后面的同步任務執(zhí)行完了再執(zhí)行
執(zhí)行順序按照微任務執(zhí)行順序來執(zhí)行
console.log("start")
async function f1(){
console.log("f1 start")
var aa= await 1;//到這里掛起,后面先不執(zhí)行
console.log("f1 end")
}
setTimeout(function(){
console.log("setTimeout ")
},0)
f1();
var pr1 =new Promise(function(resolve){
console.log(" promise start ")
resolve();
}).then(function(resoult){
console.log("promise then")
})
console.log("end")
結(jié)果: start=>f1 start=> promise start=> end=>f1 end=> promise then =>setTimeout
變形一下 async和promise交換位置
console.log("start")
async function f1(){
console.log("f1 start")
var aa= await 1;
console.log("f1 end")
}
setTimeout(function(){
console.log("setTimeout ")
},0)
var pr1 =new Promise(function(resolve){
console.log(" promise start ")
resolve();
}).then(function(resoult){
console.log("promise then")
})
f1();
console.log("end")
結(jié)果: start=>promise start=>f1 start=> end=>=> promise then =>f1 end=>setTimeout
再變形:
console.log("start")
async function f1(){
console.log("f1 start")
var aa= await 1;
setTimeout(function(){
console.log("setTimeout 2")
},0)
}
setTimeout(function(){
console.log("setTimeout ")
},0)
var pr1 =new Promise(function(resolve){
console.log(" promise start ")
resolve();
}).then(function(resoult){
console.log("promise then")
})
f1();
console.log("end")
結(jié)果: start=>promise start=> f1 start=> end=> promise then =>setTimeout=>setTimeout 2