JavaScript中的事件循環(huán)機制及其運行原理
javascript是單線程的非阻塞的腳本語言
單線程
只有一個主線程來處理任務(wù)。
非阻塞
JS引擎執(zhí)行異步任務(wù)時,不會一直等待返回結(jié)果,主線程會掛起(pending)這個任務(wù),繼續(xù)執(zhí)行其他任務(wù),當異步任務(wù)返回結(jié)果時,js將異步任務(wù)的callback放到任務(wù)隊列中,等到當前任務(wù)棧中的任務(wù)都執(zhí)行完畢,處于閑置狀態(tài)的主線程按照隊列順序?qū)㈥犑椎腸alback函數(shù)加入到執(zhí)行棧中,執(zhí)行該函數(shù)的同步代碼,如果又遇到異步任務(wù),再將其回調(diào)函數(shù)加入到隊列中–事件循環(huán)機制。
JS通常是非阻塞的,除了某些特殊情況,JS會停止代碼執(zhí)行:alert, confirm, prompt
js的任務(wù)隊列分為同步任務(wù)和異步任務(wù)
同步任務(wù)
在主線程里執(zhí)行,當瀏覽器第一遍過濾html文件的時候可以執(zhí)行完;(在當前作用域直接執(zhí)行的所有內(nèi)容,包括執(zhí)行的方法、new出來的對象)
異步任務(wù)
比較耗費時間與性能的,當瀏覽器執(zhí)行到這些的時候會將其丟到異步任務(wù)隊列中,不會立即執(zhí)行的任務(wù)
異步任務(wù)分為宏任務(wù)(macrotask) 和 微任務(wù)(microtask),執(zhí)行的優(yōu)先級不同
宏任務(wù):script, setTimeout, setInterval, setImmeditate, T/O, UI rendering
微任務(wù):process, nextTick, promise.then(), object.observe, MutationObserver, await, async
回調(diào)函數(shù)是微任務(wù),會被加入微任務(wù)隊列,回調(diào)函數(shù)是宏任務(wù),會被加入宏任務(wù)隊列,微任務(wù)優(yōu)先級高于宏任務(wù)
Event loop過程
- 主線程開始執(zhí)行一段代碼, 假設(shè)開始執(zhí)行一個 script 標簽內(nèi)的代碼,將代碼放入執(zhí)行棧中執(zhí)行,同步代碼優(yōu)先執(zhí)行,執(zhí)行過程中,當遇到任務(wù)源時,判斷是宏任務(wù)還是微任務(wù)。
- 如果是宏任務(wù),加入到宏任務(wù)隊列中,如果是微任務(wù),加入到微任務(wù)隊列中。
- 同步代碼執(zhí)行完成,執(zhí)行??臻e,檢查微任務(wù)隊列中是否有可執(zhí)行任務(wù),如果有,依次執(zhí)行所有微任務(wù)隊列中的任務(wù)。如果沒有。當前任務(wù)執(zhí)行結(jié)束。
- DOM渲染。
- 檢查宏任務(wù)隊列是否有可執(zhí)行的宏任務(wù),如果有,取出隊列中最前面的那個宏任務(wù),加入到執(zhí)行棧中開始執(zhí)行,然后重復前面步驟,直到宏任務(wù)隊列中所有任務(wù)執(zhí)行結(jié)束
微任務(wù)在DOM渲染前觸發(fā),宏任務(wù)在DOM渲染后觸發(fā)
代碼示例1
// 語句一
console.log(1);
// 語句二
setTimeout(()=>{
console.log(2);
},0);
//語句三
Promise.resolve().then(()=>{
console.log(3);
})
// 語句四
console.log(4);
//輸出順序
//1,4,3,2
代碼示例2
console.log("script start");
setTimeout(function () {
console.log("setTimeout");
}, 0);
Promise.resolve()
.then(function () {
console.log("promise1");
})
.then(function () {
console.log("promise2");
});
console.log("script end");

代碼示例3
new Promise(…)中的代碼,也是同步代碼,會立即執(zhí)行。只有then之后的代碼,才是異步執(zhí)行的代碼
console.log("script start");
setTimeout(function () {
console.log("timeout1");
}, 10);
new Promise((resolve) => {
console.log("promise1");
resolve();
setTimeout(() => console.log("timeout2"), 10);
}).then(function () {
console.log("then1");
});
console.log("script end");

代碼示例4
async 和 await 是 Generator 和 Promise 的語法糖。async 函數(shù)和普通函數(shù)一樣,只是表示這個函數(shù)里有異步操作的方法,并返回一個 Promise 對象
await后面的函數(shù)執(zhí)行完畢時,await會產(chǎn)生一個微任務(wù)(Promise.then是微任務(wù))。
// 等價
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
// Promise 寫法
async function async1() {
console.log("async1 start");
Promise.resolve(async2()).then(() => console.log("async1 end"));
}

async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
async1();
setTimeout(() => {
console.log("timeout");
}, 0);
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("promise2");
});
console.log("script end");
代碼示例5
// 1. 開始執(zhí)行
console.log(1) // 2. 打印 1
setTimeout(function () { // 6. 瀏覽器在 0ms 后,將該函數(shù)推入任務(wù)隊列
console.log(2) // 7. 打印 2
Promise.resolve(1).then(function () { // 8. 將 resolve(1) 推入任務(wù)隊列 9. 將 function函數(shù)推入任務(wù)隊列
console.log('ok') // 10. 打印 ok
})
}) // 3.調(diào)用 setTimeout 函數(shù),并定義其完成后執(zhí)行的回調(diào)函數(shù)
setTimeout(function () { // 11. 瀏覽器 0ms 后,將該函數(shù)推入任務(wù)隊列
console.log(3) // 12. 打印 3
}) // 4. 調(diào)用 setTimeout 函數(shù),并定義其完成后執(zhí)行的回調(diào)函數(shù)
// 5. 主線程執(zhí)行棧清空,開始讀取 任務(wù)隊列 中的任務(wù)
// output: 1 2 ok 3
代碼示例6
// 1. 開始執(zhí)行
console.log(1) // 2. 打印 1
setTimeout(function () { // 6. 瀏覽器在 0ms 后,將該函數(shù)推入任務(wù)隊列
console.log(2) // 7. 打印 2
Promise.resolve(1).then(function () { // 8. 將 resolve(1) 推入任務(wù)隊列 9. 將 function函數(shù)推入任務(wù)隊列
console.log('ok') // 10. 打印 ok
})
}) // 3.調(diào)用 setTimeout 函數(shù),并定義其完成后執(zhí)行的回調(diào)函數(shù)
setTimeout(function () { // 11. 瀏覽器 0ms 后,將該函數(shù)推入任務(wù)隊列
console.log(3) // 12. 打印 3
}) // 4. 調(diào)用 setTimeout 函數(shù),并定義其完成后執(zhí)行的回調(diào)函數(shù)
// 5. 主線程執(zhí)行棧清空,開始讀取 任務(wù)隊列 中的任務(wù)
// output: 1 2 ok 3代碼示例7
setTimeout(function(){
console.log('1')
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3')
});
var timer;
timer = setInterval(function(){
console.log('5');
clearInterval(timer);
});
new Promise(function(resolve){
resolve();
}).then(function(){
console.log('6')
});
console.log('4');
// 2,4,3,6,1,5
代碼示例8
Promise.resolve().then(()=>{
console.log('Promise1')
setTimeout(()=>{
console.log('setTimeout2')
},0)
});
setTimeout(()=>{
console.log('setTimeout1')
Promise.resolve().then(()=>{
console.log('Promise2')
})
},0);
console.log('start');
// start -> Promise1 -> setTimeout1 -> Promise2 - > setTimeout2
實例
<body>
<div id="box"></div>
<script src="./app.js"></script>s
</body>
//js
const box = document.getElementById('box')
box.innerHTML = '<P>我是后來插進去的內(nèi)容</P>'
console.log("1");
setTimeout( ()=>{
console.log("2");
alert("定時器執(zhí)行了")
},0 )
Promise.resolve().then(()=>{
console.log("3")
alert("Promise執(zhí)行了")
})
console.log("4");


tip:從規(guī)范來看,microtask (微任務(wù))優(yōu)先于 macrotask(宏任務(wù)) 執(zhí)行,所以如果有需要優(yōu)先執(zhí)行的邏輯,放入microtask 隊列會比 task 更早的被執(zhí)行。
到此這篇關(guān)于JavaScript中的事件循環(huán)機制及其運行原理的文章就介紹到這了,更多相關(guān)JavaScript事件循環(huán)機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript判斷元素存在和判斷元素存在于實時的dom中的方法
本文主要介紹了javascript判斷元素存在和判斷元素存在于實時的dom中的方法。具有一定的參考價值,下面跟著小編一起來看下吧2017-01-01
JavaScript獲取客戶端計算機硬件及系統(tǒng)等信息的方法
本文為大家詳細介紹下如何使用JavaScript獲取客戶端計算機硬件及系統(tǒng)等信息,下面有個不錯的示例,感興趣的朋友可以參考下2014-01-01
Javascript String對象擴展HTML編碼和解碼的方法
Javascript String對象擴展HTML編碼和解碼的代碼2009-06-06

