javascript中的Event-Loop
在之前的一篇文章中我們解釋了一下為什么JavaScript要設(shè)計成單線程以及這門語言的任務(wù)隊列的概念,這也幫助了我們簡單了解了這門語言的運行機制,那么今天我們就談?wù)勅蝿?wù)隊列相關(guān)的概念!
事件和回調(diào)函數(shù)
任務(wù)隊列其實是事件的一個隊列,也可以理解為消息隊列,當IO設(shè)備完成一個任務(wù)的時候,就會在任務(wù)隊列中添加一個事件,用來表示當前任務(wù)已經(jīng)執(zhí)行完了,可以進入執(zhí)行棧(也就是之前講過的主線程隊列)了,主線程讀取任務(wù)隊列也就是讀取有哪些事件!
任務(wù)隊列中的事件除了IO設(shè)備之外,還有用戶點擊、鍵盤事件等,只要指定過回調(diào)函數(shù),這些事件發(fā)生時就會進入任務(wù)隊列,然后等待主線程讀取。
回調(diào)函數(shù)(callback)其實就是被主線程掛起來的代碼,主線程執(zhí)行異步任務(wù),其實就是執(zhí)行回調(diào)函數(shù)!
任務(wù)隊列其實是一個先進先出的數(shù)據(jù)結(jié)構(gòu),也就是說排在前面的事件會被優(yōu)先讀取,當主線程的代碼執(zhí)行完,執(zhí)行棧被清空以后,就會立即執(zhí)行任務(wù)隊列中排在最前面的事件,但是由于定時器的功能,因此,主線程需要檢查一下執(zhí)行時間,某些事件只有等到規(guī)定的時間,才可以返回主線程!
Event Loop
主線程從任務(wù)隊列讀取事件這個過程是循環(huán)不斷的,因此整個過程又被稱為Event Loop(事件循環(huán))
setTimeout(() => {
console.log('timeout1');
}, 0)
console.log(1);
setTimeout(() => {
console.log('timeout2');
}, 0)
console.log(2);
// 1 2 timeout1 timeout2
上面代碼中有兩個定時器,定時器也會放在任務(wù)隊列中,因此我們常說在js中定時器可以模擬異步,其實是js默認會把定時器放在任務(wù)隊列,前面我們講過,js先會執(zhí)行主線程的代碼,稱為執(zhí)行棧,當執(zhí)行棧的代碼執(zhí)行結(jié)束,執(zhí)行棧清空之后才會執(zhí)行任務(wù)隊列中的代碼, 因此上面的代碼不會因為定時器在前面,而先執(zhí)行定時器,當定時器的間隔時間一致時,按照添加順序,先進任務(wù)隊列則先執(zhí)行!
NodeJs的Event Loop
NodeJs也是單線程的Event Loop,但是它區(qū)別于瀏覽器的運行環(huán)境;
在Nodejs中提供了process.nextTice()和setImmediate()兩個與任務(wù)隊列有關(guān)的方法;
process.nextTice()就是在當前執(zhí)行棧尾部添加任務(wù),也就是任務(wù)隊列(所有的異步任務(wù))開始之前;
console.log(1);
setTimeout(function timeout() {
console.log('timeout1');
}, 0);
process.nextTick(function() {
console.log(3);
process.nextTick(function(){
console.log(4);
});
});
setTimeout(function timeout() {
console.log('timeout2');
}, 0);
console.log(2);
// 1 2 3 4 timeout1 timeout2
setImmediate()會在每一次Event Loop結(jié)束執(zhí)行,或者說下一次Event Loop執(zhí)行之前執(zhí)行
console.log('start');
setTimeout(function() {
console.log('timeout1');
}, 0);
setImmediate(function (){
setImmediate(function() {
console.log(1);
setTimeout(function() {
console.log('timeout3');
}, 0);
setImmediate(function(){
console.log(2);
});
});
setTimeout(function() {
console.log('timeout2');
}, 0);
});
process.nextTick(function() {
console.log('nextTick1');
process.nextTick(function() {
console.log('nextTick2')
})
})
console.log('end');
// start end nextTick1 nextTick2 timeout1 1 timeout2 2 timeout3
上面代碼前三個輸出結(jié)果在沒有其他干擾,就目前代碼,不用質(zhì)疑,主線程肯定先執(zhí)行,接下來為nextTick,因為它會被放在所有異步執(zhí)行之前,不論是否嵌套(不包括嵌套在其他異步函數(shù)中),timeout1在主線程程中被添加到任務(wù)隊列,不論它是否在start之后還是end之前,它是區(qū)別于其他函數(shù)的唯一一個在任務(wù)隊列頂端的函數(shù),而setImmediate總會在一個Event Loop之后執(zhí)行!
下篇我們我們聊聊任務(wù)隊列中的微任務(wù)和宏任務(wù)!