成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

淺析 event-loop 事件輪詢

2501207950 / 1248人閱讀

摘要:如果執(zhí)行的準(zhǔn)備時(shí)間大于了,因?yàn)閳?zhí)行同步代碼后,定時(shí)器的回調(diào)已經(jīng)被放入隊(duì)列,所以會(huì)先執(zhí)行隊(duì)列。


閱讀原文


瀏覽器中的事件輪詢

JavaScript 是一門單線程語(yǔ)言,之所以說(shuō)是單線程,是因?yàn)樵跒g覽器中,如果是多線程,并且兩個(gè)線程同時(shí)操作了同一個(gè) Dom 元素,那最后的結(jié)果會(huì)出現(xiàn)問題。所以,JavaScript 是單線程的,但是如果完全由上至下的一行一行執(zhí)行代碼,假如一個(gè)代碼塊執(zhí)行了很長(zhǎng)的時(shí)間,后面必須要等待當(dāng)前執(zhí)行完畢,這樣的效率是非常低的,所以有了異步的概念,確切的說(shuō),JavaScript 的主線程是單線程的,但是也有其他的線程去幫我們實(shí)現(xiàn)異步操作,比如定時(shí)器線程、事件線程、Ajax 線程。

在瀏覽器中執(zhí)行 JavaScript 有兩個(gè)區(qū)域,一個(gè)是我們平時(shí)所說(shuō)的同步代碼執(zhí)行,是在棧中執(zhí)行,原則是先進(jìn)后出,而在執(zhí)行異步代碼的時(shí)候分為兩個(gè)隊(duì)列,macro-task(宏任務(wù))和 micro-task(微任務(wù)),遵循先進(jìn)先出的原則。

// 作用域鏈
function one() {
    console.log(1);
    function two() {
        console.log(2);
        function three() {
            console.log(3);
        }
        three();
    }
    two();
}
one();

// 1
// 2
// 3

上面的代碼都是同步的代碼,在執(zhí)行的時(shí)候先將全局作用域放入棧中,執(zhí)行全局作用域中的代碼,解析了函數(shù) one,當(dāng)執(zhí)行函數(shù)調(diào)用 one() 的時(shí)候?qū)?one 的作用域放入棧中,執(zhí)行 one 中的代碼,打印了 1,解析了 two,執(zhí)行 two(),將 two 放入棧中,執(zhí)行 two,打印了 2,解析了 three,執(zhí)行了 three(),將 three 放入棧中,執(zhí)行 three,打印了 3。

在函數(shù)執(zhí)行完釋放的過(guò)程中,因?yàn)槿肿饔糜蛑杏?one 正在執(zhí)行,one 中有 two 正在執(zhí)行,two 中有 three 正在執(zhí)行,所以釋放內(nèi)存時(shí)必須由內(nèi)層向外層釋放,three 執(zhí)行后釋放,此時(shí) three 不再占用 two 的執(zhí)行環(huán)境,將 two 釋放,two 不再占用 one 的執(zhí)行環(huán)境,將 one 釋放,one 不再占用全局作用域的執(zhí)行環(huán)境,最后釋放全局作用域,這就是在棧中執(zhí)行同步代碼時(shí)的先進(jìn)后出原則,更像是一個(gè)杯子,先放進(jìn)去的在最下面,需要最后取出。

而異步隊(duì)列更像時(shí)一個(gè)管道,有兩個(gè)口,從入口進(jìn),從出口出,所以是先進(jìn)先出,在宏任務(wù)隊(duì)列中代表的有 setTimeout、setIntervalsetImmediate、MessageChannel,微任務(wù)的代表為 Promise 的 then 方法、MutationObserve(已廢棄)。

案例 1

let messageChannel = new MessageChannel();
let prot2 = messageChannel.port2;

messageChannel.port1.postMessage("I love you");
console.log(1);

prot2.onmessage = function(e) {
    console.log(e.data);
};
console.log(2);

// 1
// 2
// I love you

從上面案例中可以看出,MessageChannel 是宏任務(wù),晚于同步代碼執(zhí)行。

案例 2

setTimeout(() => console.log(1), 2000);
setTimeout(() => console.log(2), 1000);
console.log(3);

// 3
// 2
// 1

上面代碼可以看出其實(shí) setTimeout 并不是在同步代碼執(zhí)行的時(shí)候就放入了異步隊(duì)列,而是等待時(shí)間到達(dá)時(shí)才會(huì)放入異步隊(duì)列,所以才會(huì)有了上面的結(jié)果。

案例 3

setImmediate(function() {
    console.log("setImmediate");
});

setTimeout(function() {
    console.log("setTimeout");
}, 0);

console.log(1);

// 1
// setTimeout
// setImmediate

同為宏任務(wù),setImmediatesetTimeout 延遲時(shí)間為 0 時(shí)是晚于 setTimeout 被放入異步隊(duì)列的,這里需要注意的是 setImmediate 在瀏覽器端,到目前為止只有 IE 實(shí)現(xiàn)了。

上面的案例都是關(guān)于宏任務(wù),下面我們舉一個(gè)有微任務(wù)的案例來(lái)看一看微任務(wù)和宏任務(wù)的執(zhí)行機(jī)制,在瀏覽器端微任務(wù)的代表其實(shí)就是 Promise 的 then 方法。

案例 4

setTimeout(() => {
    console.log("setTimeout1");
    Promise.resolve().then(data => {
        console.log("Promise1");
    });
}, 0);

Promise.resolve().then(data => {
    console.log("Promise2");
    setTimeout(() => {
        console.log("setTimeout2");
    }, 0);
});

// Promise2
// setTimeout1
// Promise1
// setTimeout2

從上面的執(zhí)行結(jié)果其實(shí)可以看出,同步代碼在棧中執(zhí)行完畢后會(huì)先去執(zhí)行微任務(wù)隊(duì)列,將微任務(wù)隊(duì)列執(zhí)行完畢后,會(huì)去執(zhí)行宏任務(wù)隊(duì)列,宏任務(wù)隊(duì)列執(zhí)行一個(gè)宏任務(wù)以后,會(huì)去看看有沒有產(chǎn)生新的微任務(wù),如果有則清空微任務(wù)隊(duì)列后再執(zhí)行下一個(gè)宏任務(wù),依次輪詢,直到清空整個(gè)異步隊(duì)列。


Node 中的事件輪詢

在 Node 中的事件輪詢機(jī)制與瀏覽器相似又不同,相似的是,同樣先在棧中執(zhí)行同步代碼,同樣是先進(jìn)后出,不同的是 Node 有自己的多個(gè)處理不同問題的階段和對(duì)應(yīng)的隊(duì)列,也有自己內(nèi)部實(shí)現(xiàn)的微任務(wù) process.nextTick,Node 的整個(gè)事件輪詢機(jī)制是 Libuv 庫(kù)實(shí)現(xiàn)的。

Node 中事件輪詢的流程如下圖:

從圖中可以看出,在 Node 中有多個(gè)隊(duì)列,分別執(zhí)行不同的操作,而每次在隊(duì)列切換的時(shí)候都去執(zhí)行一次微任務(wù)隊(duì)列,反復(fù)的輪詢。

案例 1

setTimeout(function() {
    console.log("setTimeout");
}, 0);

setImmediate(function() {
    console.log("setInmediate");
});

默認(rèn)情況下 setTimeoutsetImmediate 是不知道哪一個(gè)先執(zhí)行的,順序不固定,Node 執(zhí)行的時(shí)候有準(zhǔn)備的時(shí)間,setTimeout 延遲時(shí)間設(shè)置為 0 其實(shí)是大概 4ms,假設(shè) Node 準(zhǔn)備時(shí)間在 4ms 之內(nèi),開始執(zhí)行輪詢,定時(shí)器沒到時(shí)間,所以輪詢到下一隊(duì)列,此時(shí)要等再次循環(huán)到 timer 隊(duì)列后執(zhí)行定時(shí)器,所以會(huì)先執(zhí)行 check 隊(duì)列的 setImmediate。

如果 Node 執(zhí)行的準(zhǔn)備時(shí)間大于了 4ms,因?yàn)閳?zhí)行同步代碼后,定時(shí)器的回調(diào)已經(jīng)被放入 timer 隊(duì)列,所以會(huì)先執(zhí)行 timer 隊(duì)列。

案例 2

setTimeout(() => {
    console.log("setTimeout1");
    Promise.resolve().then(() => {
        console.log("Promise1");
    });
}, 0);

setTimeout(() => {
    console.log("setTimeout2");
}, 0);
console.log(1);

// 1
// setTimeout1
// setTimeout2
// Promise1

Node 事件輪詢中,輪詢到每一個(gè)隊(duì)列時(shí),都會(huì)將當(dāng)前隊(duì)列任務(wù)清空后,在切換下一隊(duì)列之前清空一次微任務(wù)隊(duì)列,這是與瀏覽器端不一樣的。

瀏覽器端會(huì)在宏任務(wù)隊(duì)列當(dāng)中執(zhí)行一個(gè)任務(wù)后插入執(zhí)行微任務(wù)隊(duì)列,清空微任務(wù)隊(duì)列后,再回到宏任務(wù)隊(duì)列執(zhí)行下一個(gè)宏任務(wù)。

上面案例在 Node 事件輪詢中,會(huì)將 timer 隊(duì)列清空后,在輪詢下一個(gè)隊(duì)列之前執(zhí)行微任務(wù)隊(duì)列。

案例 3

setTimeout(() => {
    console.log("setTimeout1");
}, 0);

setTimeout(() => {
    console.log("setTimeout2");
}, 0);

Promise.resolve().then(() => {
    console.log("Promise1");
});
console.log(1);

// 1
// Promise1
// setTimeout1
// setTimeout2

上面代碼的執(zhí)行過(guò)程是,先執(zhí)行棧,棧執(zhí)行時(shí)打印 1Promise.resolve() 產(chǎn)生微任務(wù),棧執(zhí)行完畢,從棧切換到 timer 隊(duì)列之前,執(zhí)行微任務(wù)隊(duì)列,再去執(zhí)行 timer 隊(duì)列。

案例 4

setImmediate(() => {
    console.log("setImmediate1");
    setTimeout(() => {
        console.log("setTimeout1");
    }, 0);
});

setTimeout(() => {
    console.log("setTimeout2");
    setImmediate(() => {
        console.log("setImmediate2");
    });
}, 0);

//結(jié)果1
// setImmediate1
// setTimeout2
// setTimeout1
// setImmediate2

// 結(jié)果2
// setTimeout2
// setImmediate1
// setImmediate2
// setTimeout1

setImmediatesetTimeout 執(zhí)行順序不固定,假設(shè) check 隊(duì)列先執(zhí)行,會(huì)執(zhí)行 setImmediate 打印 setImmediate1,將遇到的定時(shí)器放入 timer 隊(duì)列,輪詢到 timer 隊(duì)列,因?yàn)樵跅V袌?zhí)行同步代碼已經(jīng)在 timer 隊(duì)列放入了一個(gè)定時(shí)器,所以按先后順序執(zhí)行兩個(gè) setTimeout,執(zhí)行第一個(gè)定時(shí)器打印 setTimeout2,將遇到的 setImmediate 放入 check 隊(duì)列,執(zhí)行第二個(gè)定時(shí)器打印 setTimeout1,再次輪詢到 check 隊(duì)列執(zhí)行新加入的 setImmediate,打印 setImmediate2,產(chǎn)生結(jié)果 1

假設(shè) timer 隊(duì)列先執(zhí)行,會(huì)執(zhí)行 setTimeout 打印 setTimeout2,將遇到的 setImmediate 放入 check 隊(duì)列,輪詢到 check 隊(duì)列,因?yàn)樵跅V袌?zhí)行同步代碼已經(jīng)在 check 隊(duì)列放入了一個(gè) setImmediate,所以按先后順序執(zhí)行兩個(gè) setImmediate,執(zhí)行第一個(gè) setImmediate 打印 setImmediate1,將遇到的 setTimeout 放入 timer 隊(duì)列,執(zhí)行第二個(gè) setImmediate 打印 setImmediate2,再次輪詢到 timer 隊(duì)列執(zhí)行新加入的 setTimeout,打印 setTimeout1,產(chǎn)生結(jié)果 2。

案例 5

setImmediate(() => {
    console.log("setImmediate1");
    setTimeout(() => {
        console.log("setTimeout1");
    }, 0);
});

setTimeout(() => {
    process.nextTick(() => console.log("nextTick"));
    console.log("setTimeout2");
    setImmediate(() => {
        console.log("setImmediate2");
    });
}, 0);

//結(jié)果1
// setImmediate1
// setTimeout2
// setTimeout1
// nextTick
// setImmediate2

// 結(jié)果2
// setTimeout2
// nextTick
// setImmediate1
// setImmediate2
// setTimeout1

這與上面一個(gè)案例類似,不同的是在 setTimeout 執(zhí)行的時(shí)候產(chǎn)生了一個(gè)微任務(wù) nextTick,我們只要知道,在 Node 事件輪詢中,在切換隊(duì)列時(shí)要先去執(zhí)行微任務(wù)隊(duì)列,無(wú)論是 check 隊(duì)列先執(zhí)行,還是 timer 隊(duì)列先執(zhí)行,都會(huì)很容易分析出上面的兩個(gè)結(jié)果。

案例 6

const fs = require("fs");

fs.readFile("./.gitignore", "utf8", function() {
    setTimeout(() => {
        console.log("timeout");
    }, 0);
    setImmediate(function() {
        console.log("setImmediate");
    });
});

// setImmediate
// timeout

上面案例的 setTimeoutsetImmediate 的執(zhí)行順序是固定的,前面都是不固定的,這是為什么?

因?yàn)榍懊娴牟还潭ㄊ窃跅V袌?zhí)行同步代碼時(shí)就遇到了 setTimeoutsetImmediate,因?yàn)闊o(wú)法判斷 Node 的準(zhǔn)備時(shí)間,不確定準(zhǔn)備結(jié)束定時(shí)器是否到時(shí)并加入 timer 隊(duì)列。

而上面代碼明顯可以看出 Node 準(zhǔn)備結(jié)束后會(huì)直接執(zhí)行 poll 隊(duì)列進(jìn)行文件的讀取,在回調(diào)中將 setTimeoutsetImmediate 分別加入 timer 隊(duì)列和 check 隊(duì)列,Node 隊(duì)列的輪詢是有順序的,在 poll 隊(duì)列后應(yīng)該先切換到 check 隊(duì)列,然后再重新輪詢到 timer 隊(duì)列,所以得到上面的結(jié)果。

案例 7

Promise.resolve().then(() => console.log("Promise"));
process.nextTick(() => console.log("nextTick"));

// nextTick
// Promise

在 Node 中有兩個(gè)微任務(wù),Promisethen 方法和 process.nextTick,從上面案例的結(jié)果我們可以看出,在微任務(wù)隊(duì)列中 process.nextTick 是優(yōu)先執(zhí)行的。

上面內(nèi)容就是瀏覽器與 Node 在事件輪詢的規(guī)則,相信在讀完以后應(yīng)該已經(jīng)徹底弄清了瀏覽器的事件輪詢機(jī)制和 Node 的事件輪詢機(jī)制,并深刻的體會(huì)到了他們之間的相同和不同。


文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/98286.html

相關(guān)文章

  • 理解Event-Loop

    摘要:回調(diào)函數(shù)任務(wù)完成的時(shí)候,需要執(zhí)行哪段代碼來(lái)處理呢當(dāng)然是回調(diào)函數(shù)了。事件處理器和回調(diào)函數(shù)類似。但是特定的事件處理器在瀏覽器進(jìn)入異步事件驅(qū)動(dòng)階段時(shí)就會(huì)針對(duì)特定的事件注冊(cè)。當(dāng)事件對(duì)象返回到執(zhí)行線程時(shí),事件處理器也會(huì)同時(shí)進(jìn)入執(zhí)行棧中執(zhí)行。 Event Loop(事件輪詢)機(jī)制是一個(gè)經(jīng)常把人搞暈的東東。我不敢說(shuō)我完全明白,只是在此談?wù)勎业臏\見。 事件的處理 瀏覽器是一個(gè)事件驅(qū)動(dòng)(event-dr...

    blair 評(píng)論0 收藏0
  • 從網(wǎng)絡(luò)IO到Thrift網(wǎng)絡(luò)模型

    摘要:基本原理函數(shù)監(jiān)視的文件描述符分類,分別是和。具體模型如下與模式相比,在完成數(shù)據(jù)讀取之后,將業(yè)務(wù)處理過(guò)程交由一個(gè)線程池來(lái)完成,主線程直接返回進(jìn)行下一次循環(huán)操作,效率大大提升。是提供的最高效的網(wǎng)絡(luò)模型。 I/O多路復(fù)用 IO多路復(fù)用就是通過(guò)一種機(jī)制,一個(gè)進(jìn)程可以監(jiān)聽多個(gè)文件描述符,一個(gè)某個(gè)描述符就緒(一般是讀就緒或?qū)懢途w),就能夠通知程序進(jìn)行相應(yīng)的讀寫操作。select、poll、epol...

    馬永翠 評(píng)論0 收藏0
  • JavaScript從初級(jí)往高級(jí)走系列————異步

    摘要:之所以是單線程,取決于它的實(shí)際使用,例如不可能同添加一個(gè)和刪除這個(gè),所以它只能是單線程的。所以,這個(gè)新標(biāo)準(zhǔn)并沒有改變單線程的本質(zhì)。 原文博客地址:https://finget.github.io/2018/05/21/async/ 異步 什么是單線程,和異步有什么關(guān)系 什么是event-loop 是否用過(guò)jQuery的Deferred Promise的基本使用和原理 介紹一下asyn...

    andot 評(píng)論0 收藏0
  • Java NIO淺析

    摘要:阻塞請(qǐng)求結(jié)果返回之前,當(dāng)前線程被掛起。也就是說(shuō)在異步中,不會(huì)對(duì)用戶線程產(chǎn)生任何阻塞。當(dāng)前線程在拿到此次請(qǐng)求結(jié)果的過(guò)程中,可以做其它事情。事實(shí)上,可以只用一個(gè)線程處理所有的通道。 準(zhǔn)備知識(shí) 同步、異步、阻塞、非阻塞 同步和異步說(shuō)的是服務(wù)端消息的通知機(jī)制,阻塞和非阻塞說(shuō)的是客戶端線程的狀態(tài)。已客戶端一次網(wǎng)絡(luò)請(qǐng)求為例做簡(jiǎn)單說(shuō)明: 同步同步是指一次請(qǐng)求沒有得到結(jié)果之前就不返回。 異步請(qǐng)求不會(huì)...

    yeooo 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<