JS的Event-Loop

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ù)!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容