詳解JavaScript事件循環(huán)
JavaScript事件循環(huán)是一種機(jī)制,用于處理異步事件和回調(diào)函數(shù)。它是JavaScript運(yùn)行時(shí)環(huán)境的一部分,負(fù)責(zé)管理事件隊(duì)列和調(diào)用棧。
事件循環(huán)的基本原理是事件循環(huán)的核心是一個(gè)事件隊(duì)列,所有的事件都被放入這個(gè)隊(duì)列中,然后按照順序依次執(zhí)行。如果隊(duì)列為空,JavaScript會(huì)等待新的任務(wù)加入隊(duì)列。當(dāng)JavaScript代碼執(zhí)行時(shí),所有同步任務(wù)都會(huì)被立即執(zhí)行,而異步任務(wù)則會(huì)被放入事件隊(duì)列中。
當(dāng)所有同步任務(wù)執(zhí)行完畢后,事件循環(huán)會(huì)從事件隊(duì)列中取出一個(gè)任務(wù),并將其放入調(diào)用棧中執(zhí)行。當(dāng)該任務(wù)執(zhí)行完畢后,事件循環(huán)會(huì)再次從事件隊(duì)列中取出下一個(gè)任務(wù),并重復(fù)這個(gè)過程。
一、事件循環(huán)的執(zhí)行過程
1、執(zhí)行同步代碼,直到遇到第一個(gè)異步事件(如setTimeout、setInterval、Promise等)。
2、將異步事件放入事件隊(duì)列中,并繼續(xù)執(zhí)行同步代碼。
3、當(dāng)所有同步代碼執(zhí)行完畢后,JavaScript引擎會(huì)檢查事件隊(duì)列中是否有事件需要執(zhí)行。
4、如果事件隊(duì)列中有事件需要執(zhí)行,JavaScript引擎會(huì)將第一個(gè)事件取出來,并執(zhí)行對(duì)應(yīng)的回調(diào)函數(shù)。
5、執(zhí)行完回調(diào)函數(shù)后,JavaScript引擎會(huì)再次檢查事件隊(duì)列中是否有事件需要執(zhí)行,如果有則重復(fù)步驟4,否則繼續(xù)等待新的事件加入事件隊(duì)列。
需要注意的是,事件循環(huán)是單線程的,也就是說JavaScript引擎在同一時(shí)間只能執(zhí)行一個(gè)任務(wù)。
因此,如果一個(gè)任務(wù)執(zhí)行時(shí)間過長(zhǎng),會(huì)阻塞事件循環(huán),導(dǎo)致其他任務(wù)無(wú)法執(zhí)行。為了避免這種情況,可以將長(zhǎng)時(shí)間的任務(wù)拆分成多個(gè)小任務(wù),使用setTimeout或setInterval分批執(zhí)行。
另外,Promise也是基于事件循環(huán)的機(jī)制實(shí)現(xiàn)的。當(dāng)Promise狀態(tài)發(fā)生改變時(shí),會(huì)將對(duì)應(yīng)的回調(diào)函數(shù)放入微任務(wù)隊(duì)列中,等待當(dāng)前任務(wù)執(zhí)行完畢后立即執(zhí)行。因此,Promise的回調(diào)函數(shù)總是在當(dāng)前任務(wù)執(zhí)行完畢后立即執(zhí)行,而不會(huì)被放入事件隊(duì)列中等待執(zhí)行。
二、事件循環(huán)進(jìn)階用法
1、Promise:Promise是一種異步編程的解決方案,它可以將異步操作轉(zhuǎn)化為同步操作的形式,使得代碼更加簡(jiǎn)潔易懂。Promise的基本原理是將異步操作封裝成一個(gè)Promise對(duì)象,通過then方法來處理異步操作的結(jié)果。
2、async/await:async/await是ES2017引入的一種異步編程的解決方案,它可以讓異步代碼看起來像同步代碼,使得代碼更加易讀易懂。async/await的基本原理是使用async關(guān)鍵字定義一個(gè)異步函數(shù),然后在函數(shù)內(nèi)部使用await關(guān)鍵字來等待異步操作的結(jié)果。
3、定時(shí)器:JavaScript中有兩種定時(shí)器,分別是setTimeout和setInterval。setTimeout用于在指定的時(shí)間后執(zhí)行一次任務(wù),而setInterval則用于每隔一段時(shí)間執(zhí)行一次任務(wù)。這兩種定時(shí)器都是異步任務(wù),會(huì)被放入任務(wù)隊(duì)列中等待執(zhí)行。
三、JavaScript任務(wù)類型
JavaScript中的任務(wù)按照不同緯度進(jìn)行區(qū)分主要分為同步任務(wù)和異步任務(wù)、宏任務(wù)和微任務(wù)。
3.1 同步任務(wù)&異步任務(wù)
1、 同步任務(wù)按照代碼順序執(zhí)行的任務(wù)。
2、 異步任務(wù)是在將來某個(gè)時(shí)間點(diǎn)執(zhí)行的任務(wù)。異步任務(wù)通常是由事件觸發(fā)器(如鼠標(biāo)點(diǎn)擊、網(wǎng)絡(luò)請(qǐng)求等)生成的。 當(dāng)一個(gè)異步任務(wù)被觸發(fā)時(shí),它會(huì)被放入任務(wù)隊(duì)列中等待執(zhí)行。
JavaScript事件循環(huán)的執(zhí)行順序可以用以下偽代碼表示:
while (queue.waitForMessage()) {
queue.processNextMessage();
} 在這個(gè)偽代碼中,queue.waitForMessage()表示等待隊(duì)列中有待執(zhí)行的任務(wù),queue.processNextMessage()表示取出隊(duì)列中的下一個(gè)任務(wù)并執(zhí)行。
需要注意的是,JavaScript事件循環(huán)的執(zhí)行順序是不可中斷的。這意味著當(dāng)一個(gè)任務(wù)正在執(zhí)行時(shí),其他任務(wù)必須等待,直到當(dāng)前任務(wù)完成。因此,如果一個(gè)任務(wù)執(zhí)行時(shí)間過長(zhǎng),會(huì)導(dǎo)致其他任務(wù)無(wú)法及時(shí)執(zhí)行,從而影響應(yīng)用程序的性能和響應(yīng)速度。
3.2 宏任務(wù)&微任務(wù)
1、宏任務(wù)包括setTimeout、setInterval、I/O操作等。
2、微任務(wù)包括Promise、MutationObserver等。
當(dāng)事件循環(huán)從事件隊(duì)列中取出一個(gè)任務(wù)時(shí),它會(huì)先執(zhí)行所有微任務(wù),然后再執(zhí)行一個(gè)宏任務(wù)。這個(gè)過程會(huì)一直重復(fù),直到事件隊(duì)列中的所有任務(wù)都被執(zhí)行完畢。
下面是一個(gè)例子:
console.log('1');
setTimeout(function() {
console.log('2');
Promise.resolve().then(function() {
console.log('3');
});
}, 0);
Promise.resolve().then(function() {
console.log('4');
});
console.log('5');
// 輸出結(jié)果為: 1 5 4 2 3 執(zhí)行順序:
- 執(zhí)行第一個(gè)
console.log,輸出1。 - 執(zhí)行
setTimeout,將其回調(diào)函數(shù)放入宏任務(wù)隊(duì)列中。 - 執(zhí)行
Promise.resolve().then,將其回調(diào)函數(shù)放入微任務(wù)隊(duì)列中。 - 執(zhí)行第二個(gè)
console.log,輸出5。 - 當(dāng)前任務(wù)執(zhí)行結(jié)束,執(zhí)行微任務(wù)隊(duì)列中的所有任務(wù),輸出4。
- 執(zhí)行宏任務(wù)隊(duì)列中的第一個(gè)任務(wù),即
setTimeout的回調(diào)函數(shù),輸出2。 - 執(zhí)行
Promise.resolve().then的回調(diào)函數(shù),輸出3。
需要注意的是,微任務(wù)和宏任務(wù)的執(zhí)行順序是固定的,即先執(zhí)行所有微任務(wù),再執(zhí)行宏任務(wù)。因此,如果在一個(gè)宏任務(wù)中產(chǎn)生了新的微任務(wù),那么這些微任務(wù)會(huì)在下一個(gè)宏任務(wù)執(zhí)行之前執(zhí)行。
下面是一個(gè)例子:
console.log('1');
setTimeout(function() {
console.log('2');
Promise.resolve().then(function() {
console.log('3');
});
}, 0);
Promise.resolve().then(function() {
console.log('4');
setTimeout(function() {
console.log('5');
}, 0);
});
console.log('6');
// 輸出結(jié)果為:1 6 4 2 3 5執(zhí)行順序:
- 執(zhí)行第一個(gè)
console.log,輸出1。 - 執(zhí)行
setTimeout,將其回調(diào)函數(shù)放入宏任務(wù)隊(duì)列中。 - 執(zhí)行
Promise.resolve().then,將其回調(diào)函數(shù)放入微任務(wù)隊(duì)列中。 - 執(zhí)行第三個(gè)
console.log,輸出6。 - 當(dāng)前任務(wù)執(zhí)行結(jié)束,執(zhí)行微任務(wù)隊(duì)列中的所有任務(wù),輸出4。
- 執(zhí)行宏任務(wù)隊(duì)列中的第一個(gè)任務(wù),即
setTimeout的回調(diào)函數(shù),輸出2。 - 執(zhí)行
Promise.resolve().then的回調(diào)函數(shù),將setTimeout的回調(diào)函數(shù)放入宏任務(wù)隊(duì)列中。 - 當(dāng)前任務(wù)執(zhí)行結(jié)束,執(zhí)行微任務(wù)隊(duì)列中的所有任務(wù),輸出3。
- 執(zhí)行宏任務(wù)隊(duì)列中的第一個(gè)任務(wù),即
setTimeout的回調(diào)函數(shù),輸出5。
到此這篇關(guān)于詳解JavaScript事件循環(huán)的文章就介紹到這了,更多相關(guān)JavaScript事件循環(huán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
js實(shí)現(xiàn)仿網(wǎng)易點(diǎn)擊彈出提示同時(shí)背景變暗效果
這篇文章主要介紹了js實(shí)現(xiàn)仿網(wǎng)易點(diǎn)擊彈出提示同時(shí)背景變暗效果,涉及javascript彈出框及頁(yè)面元素樣式操作的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-08-08
Javascript頁(yè)面跳轉(zhuǎn)常見實(shí)現(xiàn)方式匯總
這篇文章主要介紹了Javascript頁(yè)面跳轉(zhuǎn)常見實(shí)現(xiàn)方式,結(jié)合實(shí)例匯總分析了JavaScript常用的七種頁(yè)面跳轉(zhuǎn)實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
asp.net+js 實(shí)現(xiàn)無(wú)刷新上傳解析csv文件的代碼
無(wú)刷新上傳解析csv文件的實(shí)現(xiàn)代碼,需要的朋友可以參考下。2010-05-05
新人報(bào)道,發(fā)個(gè)小技巧(js數(shù)組重復(fù)判斷)
js數(shù)組重復(fù)判斷2008-10-10
javascript實(shí)現(xiàn)拖拽并替換網(wǎng)頁(yè)塊元素
實(shí)現(xiàn)類似于學(xué)生換座位的效果,將網(wǎng)頁(yè)內(nèi)的兩個(gè)元素通過拖拽的方式互換。2009-11-11

