JavaScript中的事件循環(huán)機(jī)制及其運(yùn)行原理
javascript是單線程的非阻塞的腳本語言
單線程
只有一個(gè)主線程來處理任務(wù)。
非阻塞
JS引擎執(zhí)行異步任務(wù)時(shí),不會(huì)一直等待返回結(jié)果,主線程會(huì)掛起(pending)這個(gè)任務(wù),繼續(xù)執(zhí)行其他任務(wù),當(dāng)異步任務(wù)返回結(jié)果時(shí),js將異步任務(wù)的callback放到任務(wù)隊(duì)列中,等到當(dāng)前任務(wù)棧中的任務(wù)都執(zhí)行完畢,處于閑置狀態(tài)的主線程按照隊(duì)列順序?qū)㈥?duì)首的calback函數(shù)加入到執(zhí)行棧中,執(zhí)行該函數(shù)的同步代碼,如果又遇到異步任務(wù),再將其回調(diào)函數(shù)加入到隊(duì)列中–事件循環(huán)機(jī)制。
JS通常是非阻塞的,除了某些特殊情況,JS會(huì)停止代碼執(zhí)行:alert, confirm, prompt
js的任務(wù)隊(duì)列分為同步任務(wù)和異步任務(wù)
同步任務(wù)
在主線程里執(zhí)行,當(dāng)瀏覽器第一遍過濾html文件的時(shí)候可以執(zhí)行完;(在當(dāng)前作用域直接執(zhí)行的所有內(nèi)容,包括執(zhí)行的方法、new出來的對(duì)象)
異步任務(wù)
比較耗費(fèi)時(shí)間與性能的,當(dāng)瀏覽器執(zhí)行到這些的時(shí)候會(huì)將其丟到異步任務(wù)隊(duì)列中,不會(huì)立即執(zhí)行的任務(wù)
異步任務(wù)分為宏任務(wù)(macrotask) 和 微任務(wù)(microtask),執(zhí)行的優(yōu)先級(jí)不同
宏任務(wù):script, setTimeout, setInterval, setImmeditate, T/O, UI rendering
微任務(wù):process, nextTick, promise.then(), object.observe, MutationObserver, await, async
回調(diào)函數(shù)是微任務(wù),會(huì)被加入微任務(wù)隊(duì)列,回調(diào)函數(shù)是宏任務(wù),會(huì)被加入宏任務(wù)隊(duì)列,微任務(wù)優(yōu)先級(jí)高于宏任務(wù)
Event loop過程
- 主線程開始執(zhí)行一段代碼, 假設(shè)開始執(zhí)行一個(gè) script 標(biāo)簽內(nèi)的代碼,將代碼放入執(zhí)行棧中執(zhí)行,同步代碼優(yōu)先執(zhí)行,執(zhí)行過程中,當(dāng)遇到任務(wù)源時(shí),判斷是宏任務(wù)還是微任務(wù)。
- 如果是宏任務(wù),加入到宏任務(wù)隊(duì)列中,如果是微任務(wù),加入到微任務(wù)隊(duì)列中。
- 同步代碼執(zhí)行完成,執(zhí)行棧空閑,檢查微任務(wù)隊(duì)列中是否有可執(zhí)行任務(wù),如果有,依次執(zhí)行所有微任務(wù)隊(duì)列中的任務(wù)。如果沒有。當(dāng)前任務(wù)執(zhí)行結(jié)束。
- DOM渲染。
- 檢查宏任務(wù)隊(duì)列是否有可執(zhí)行的宏任務(wù),如果有,取出隊(duì)列中最前面的那個(gè)宏任務(wù),加入到執(zhí)行棧中開始執(zhí)行,然后重復(fù)前面步驟,直到宏任務(wù)隊(duì)列中所有任務(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(…)中的代碼,也是同步代碼,會(huì)立即執(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ù)一樣,只是表示這個(gè)函數(shù)里有異步操作的方法,并返回一個(gè) Promise 對(duì)象
await后面的函數(shù)執(zhí)行完畢時(shí),await會(huì)產(chǎn)生一個(gè)微任務(wù)(Promise.then是微任務(wù))。
// 等價(jià) 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ù)隊(duì)列 console.log(2) // 7. 打印 2 Promise.resolve(1).then(function () { // 8. 將 resolve(1) 推入任務(wù)隊(duì)列 9. 將 function函數(shù)推入任務(wù)隊(duì)列 console.log('ok') // 10. 打印 ok }) }) // 3.調(diào)用 setTimeout 函數(shù),并定義其完成后執(zhí)行的回調(diào)函數(shù) setTimeout(function () { // 11. 瀏覽器 0ms 后,將該函數(shù)推入任務(wù)隊(duì)列 console.log(3) // 12. 打印 3 }) // 4. 調(diào)用 setTimeout 函數(shù),并定義其完成后執(zhí)行的回調(diào)函數(shù) // 5. 主線程執(zhí)行棧清空,開始讀取 任務(wù)隊(duì)列 中的任務(wù) // output: 1 2 ok 3
代碼示例6
// 1. 開始執(zhí)行 console.log(1) // 2. 打印 1 setTimeout(function () { // 6. 瀏覽器在 0ms 后,將該函數(shù)推入任務(wù)隊(duì)列 console.log(2) // 7. 打印 2 Promise.resolve(1).then(function () { // 8. 將 resolve(1) 推入任務(wù)隊(duì)列 9. 將 function函數(shù)推入任務(wù)隊(duì)列 console.log('ok') // 10. 打印 ok }) }) // 3.調(diào)用 setTimeout 函數(shù),并定義其完成后執(zhí)行的回調(diào)函數(shù) setTimeout(function () { // 11. 瀏覽器 0ms 后,將該函數(shù)推入任務(wù)隊(duì)列 console.log(3) // 12. 打印 3 }) // 4. 調(diào)用 setTimeout 函數(shù),并定義其完成后執(zhí)行的回調(diào)函數(shù) // 5. 主線程執(zhí)行棧清空,開始讀取 任務(wù)隊(duì)列 中的任務(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
實(shí)例
<body> <div id="box"></div> <script src="./app.js"></script>s </body> //js const box = document.getElementById('box') box.innerHTML = '<P>我是后來插進(jìn)去的內(nèi)容</P>' console.log("1"); setTimeout( ()=>{ console.log("2"); alert("定時(shí)器執(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 隊(duì)列會(huì)比 task 更早的被執(zhí)行。
到此這篇關(guān)于JavaScript中的事件循環(huán)機(jī)制及其運(yùn)行原理的文章就介紹到這了,更多相關(guān)JavaScript事件循環(huán)機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于three.js編寫的一個(gè)項(xiàng)目類示例代碼
這篇文章主要給大家介紹了關(guān)于基于three.js編寫的一個(gè)項(xiàng)目類的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01JavaScript檢查數(shù)字是否為整數(shù)或浮點(diǎn)數(shù)的方法
這篇文章主要介紹了JavaScript檢查數(shù)字是否為整數(shù)或浮點(diǎn)數(shù)的方法,涉及javascript類型判斷的相關(guān)技巧,需要的朋友可以參考下2015-06-06限制文本框只能輸入數(shù)字||只能是數(shù)字和小數(shù)點(diǎn)||只能是整數(shù)和浮點(diǎn)數(shù)
這篇文章主要介紹了限制文本框只能輸入數(shù)字||只能是數(shù)字和小數(shù)點(diǎn)||只能是整數(shù)和浮點(diǎn)數(shù)的實(shí)例代碼,非常不錯(cuò),也比較實(shí)用,需要的小伙伴一起看下吧2016-05-05小程序?qū)崿F(xiàn)輪播每次顯示三條數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了小程序?qū)崿F(xiàn)輪播每次顯示三條數(shù)據(jù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06解決js相同的正則多次調(diào)用test()返回的值卻不同的問題
今天小編就為大家分享一篇解決js相同的正則多次調(diào)用test()返回的值卻不同的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-10-10echarts實(shí)現(xiàn)橫向和縱向滾動(dòng)條(使用dataZoom)
這篇文章主要給大家介紹了關(guān)于echarts使用dataZoom實(shí)現(xiàn)橫向和縱向滾動(dòng)條的相關(guān)資料,最近項(xiàng)目中使用到echarts圖表,當(dāng)數(shù)據(jù)過多時(shí)需要添加橫向滾動(dòng)條,需要的朋友可以參考下2023-08-08