国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

淺析 event-loop 事件輪詢

2501207950 / 1225人閱讀

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


閱讀原文


瀏覽器中的事件輪詢

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

在瀏覽器中執(zhí)行 JavaScript 有兩個區(qū)域,一個是我們平時所說的同步代碼執(zhí)行,是在棧中執(zhí)行,原則是先進(jìn)后出,而在執(zhí)行異步代碼的時候分為兩個隊(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í)行的時候先將全局作用域放入棧中,執(zhí)行全局作用域中的代碼,解析了函數(shù) one,當(dāng)執(zhí)行函數(shù)調(diào)用 one() 的時候?qū)?one 的作用域放入棧中,執(zhí)行 one 中的代碼,打印了 1,解析了 two,執(zhí)行 two(),將 two 放入棧中,執(zhí)行 two,打印了 2,解析了 three,執(zhí)行了 three(),將 three 放入棧中,執(zhí)行 three,打印了 3

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

而異步隊(duì)列更像時一個管道,有兩個口,從入口進(jìn),從出口出,所以是先進(jìn)先出,在宏任務(wù)隊(duì)列中代表的有 setTimeoutsetIntervalsetImmediateMessageChannel,微任務(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í)行的時候就放入了異步隊(duì)列,而是等待時間到達(dá)時才會放入異步隊(duì)列,所以才會有了上面的結(jié)果。

案例 3

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

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

console.log(1);

// 1
// setTimeout
// setImmediate

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

上面的案例都是關(guān)于宏任務(wù),下面我們舉一個有微任務(wù)的案例來看一看微任務(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í)行完畢后會先去執(zhí)行微任務(wù)隊(duì)列,將微任務(wù)隊(duì)列執(zhí)行完畢后,會去執(zhí)行宏任務(wù)隊(duì)列,宏任務(wù)隊(duì)列執(zhí)行一個宏任務(wù)以后,會去看看有沒有產(chǎn)生新的微任務(wù),如果有則清空微任務(wù)隊(duì)列后再執(zhí)行下一個宏任務(wù),依次輪詢,直到清空整個異步隊(duì)列。


Node 中的事件輪詢

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

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

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

案例 1

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

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

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

如果 Node 執(zhí)行的準(zhǔn)備時間大于了 4ms,因?yàn)閳?zhí)行同步代碼后,定時器的回調(diào)已經(jīng)被放入 timer 隊(duì)列,所以會先執(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 事件輪詢中,輪詢到每一個隊(duì)列時,都會將當(dāng)前隊(duì)列任務(wù)清空后,在切換下一隊(duì)列之前清空一次微任務(wù)隊(duì)列,這是與瀏覽器端不一樣的。

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

上面案例在 Node 事件輪詢中,會將 timer 隊(duì)列清空后,在輪詢下一個隊(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í)行過程是,先執(zhí)行棧,棧執(zhí)行時打印 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í)行,會執(zhí)行 setImmediate 打印 setImmediate1,將遇到的定時器放入 timer 隊(duì)列,輪詢到 timer 隊(duì)列,因?yàn)樵跅V袌?zhí)行同步代碼已經(jīng)在 timer 隊(duì)列放入了一個定時器,所以按先后順序執(zhí)行兩個 setTimeout,執(zhí)行第一個定時器打印 setTimeout2,將遇到的 setImmediate 放入 check 隊(duì)列,執(zhí)行第二個定時器打印 setTimeout1,再次輪詢到 check 隊(duì)列執(zhí)行新加入的 setImmediate,打印 setImmediate2,產(chǎn)生結(jié)果 1

假設(shè) timer 隊(duì)列先執(zhí)行,會執(zhí)行 setTimeout 打印 setTimeout2,將遇到的 setImmediate 放入 check 隊(duì)列,輪詢到 check 隊(duì)列,因?yàn)樵跅V袌?zhí)行同步代碼已經(jīng)在 check 隊(duì)列放入了一個 setImmediate,所以按先后順序執(zhí)行兩個 setImmediate,執(zhí)行第一個 setImmediate 打印 setImmediate1,將遇到的 setTimeout 放入 timer 隊(duì)列,執(zhí)行第二個 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

這與上面一個案例類似,不同的是在 setTimeout 執(zhí)行的時候產(chǎn)生了一個微任務(wù) nextTick,我們只要知道,在 Node 事件輪詢中,在切換隊(duì)列時要先去執(zhí)行微任務(wù)隊(duì)列,無論是 check 隊(duì)列先執(zhí)行,還是 timer 隊(duì)列先執(zhí)行,都會很容易分析出上面的兩個結(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í)行同步代碼時就遇到了 setTimeoutsetImmediate,因?yàn)闊o法判斷 Node 的準(zhǔn)備時間,不確定準(zhǔn)備結(jié)束定時器是否到時并加入 timer 隊(duì)列。

而上面代碼明顯可以看出 Node 準(zhǔn)備結(jié)束后會直接執(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 中有兩個微任務(wù),Promisethen 方法和 process.nextTick,從上面案例的結(jié)果我們可以看出,在微任務(wù)隊(duì)列中 process.nextTick 是優(yōu)先執(zhí)行的。

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


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

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/98286.html

相關(guān)文章

  • 理解Event-Loop

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

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

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

    馬永翠 評論0 收藏0
  • JavaScript從初級往高級走系列————異步

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

    andot 評論0 收藏0
  • Java NIO淺析

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

    yeooo 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<