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

資訊專欄INFORMATION COLUMN

總結(jié):JavaScript異步、事件循環(huán)與消息隊(duì)列、微任務(wù)與宏任務(wù)

qianfeng / 3888人閱讀

摘要:?jiǎn)尉€程異步非阻塞然后,這又牽扯到了事件循環(huán)消息隊(duì)列,還有微任務(wù)宏任務(wù)這些。此步的位置不確定某個(gè)時(shí)刻后,定時(shí)器觸發(fā)線程通知事件觸發(fā)線程,事件觸發(fā)線程將回調(diào)函數(shù)加入消息隊(duì)列隊(duì)尾,等待引擎線程執(zhí)行。

前言

Philip Roberts 在演講 great talk at JSConf on the event loop 中說(shuō):要是用一句話來(lái)形容 JavaScript,我可能會(huì)這樣:

“JavaScript 是單線程、異步、非阻塞、解釋型腳本語(yǔ)言。”

單線程 ?

異步 ? ?

非阻塞 ? ? ?

然后,這又牽扯到了事件循環(huán)、消息隊(duì)列,還有微任務(wù)、宏任務(wù)這些。

作為一個(gè)初學(xué)者,對(duì)這些了解甚少。

這幾天翻閱了不少資料,似乎了解到了一二,是時(shí)候總結(jié)一下了,它們困擾了我好一段時(shí)間,就像學(xué)高數(shù)那會(huì)兒自己去理解一個(gè)概念一樣。

單線程與多線程

單線程語(yǔ)言:JavaScript 的設(shè)計(jì)就是為了處理瀏覽器網(wǎng)頁(yè)的交互(DOM操作的處理、UI動(dòng)畫等),決定了它是一門單線程語(yǔ)言。

如果有多個(gè)線程,它們同時(shí)在操作 DOM,那網(wǎng)頁(yè)將會(huì)一團(tuán)糟。

JavaScript 是單線程的,那么處理任務(wù)是一件接著一件處理,從上往下順序執(zhí)行:

console.log("script start")
console.log("do something...")
console.log("script end")

// script start
// do something...
// script end

上面的代碼會(huì)依次打印: "script start" >> "do something..." >> "script end"

那如果一個(gè)任務(wù)的處理耗時(shí)(或者是等待)很久的話,如:網(wǎng)絡(luò)請(qǐng)求、定時(shí)器、等待鼠標(biāo)點(diǎn)擊等,后面的任務(wù)也就會(huì)被阻塞,也就是說(shuō)會(huì)阻塞所有的用戶交互(按鈕、滾動(dòng)條等),會(huì)帶來(lái)極不友好的體驗(yàn)。

但是:

console.log("script start")

console.log("do something...")

setTimeout(() => {
  console.log("timer over")
}, 1000)

// 點(diǎn)擊頁(yè)面
console.log("click page")

console.log("script end")

// script start
// do something...
// click page
// script end
// timer over

"timer over""script end" 后再打印,也就是說(shuō)計(jì)時(shí)器并沒有阻塞后面的代碼。那,發(fā)生了什么?

其實(shí),JavaScript 單線程指的是瀏覽器中負(fù)責(zé)解釋和執(zhí)行 JavaScript 代碼的只有一個(gè)線程,即為JS引擎線程,但是瀏覽器的渲染進(jìn)程是提供多個(gè)線程的,如下:

JS引擎線程

事件觸發(fā)線程

定時(shí)觸發(fā)器線程

異步http請(qǐng)求線程

GUI渲染線程

瀏覽器渲染進(jìn)程參考這里

當(dāng)遇到計(jì)時(shí)器、DOM事件監(jiān)聽或者是網(wǎng)絡(luò)請(qǐng)求的任務(wù)時(shí),JS引擎會(huì)將它們直接交給 webapi,也就是瀏覽器提供的相應(yīng)線程(如定時(shí)器線程為setTimeout計(jì)時(shí)、異步http請(qǐng)求線程處理網(wǎng)絡(luò)請(qǐng)求)去處理,而JS引擎線程繼續(xù)后面的其他任務(wù),這樣便實(shí)現(xiàn)了 異步非阻塞

定時(shí)器觸發(fā)線程也只是為 setTimeout(..., 1000) 定時(shí)而已,時(shí)間一到,還會(huì)把它對(duì)應(yīng)的回調(diào)函數(shù)(callback)交給 消息隊(duì)列 去維護(hù),JS引擎線程會(huì)在適當(dāng)?shù)臅r(shí)候去消息隊(duì)列取出消息并執(zhí)行。

JS引擎線程什么時(shí)候去處理呢?消息隊(duì)列又是什么?

這里,JavaScript 通過(guò) 事件循環(huán) event loop 的機(jī)制來(lái)解決這個(gè)問題。

這個(gè)放在后面再討論吧!

同步與異步

上面說(shuō)到了異步,JavaScript 中有同步代碼與異步代碼。

下面便是同步:

console.log("hello 0")

console.log("hello 1")

console.log("hello 2")

// hello 0
// hello 1
// hello 2

它們會(huì)依次執(zhí)行,執(zhí)行完了后便會(huì)返回結(jié)果(打印結(jié)果)。

setTimeout(() => {
  console.log("hello 0")
}, 1000)

console.log("hello 1")

// hello 1
// hello 0

上面的 setTimeout 函數(shù)便不會(huì)立刻返回結(jié)果,而是發(fā)起了一個(gè)異步,setTimeout 便是異步的發(fā)起函數(shù)或者是注冊(cè)函數(shù),() => {...} 便是異步的回調(diào)函數(shù)。

這里,JS引擎線程只會(huì)關(guān)心異步的發(fā)起函數(shù)是誰(shuí)、回調(diào)函數(shù)是什么?并將異步交給 webapi 去處理,然后繼續(xù)執(zhí)行其他任務(wù)。

異步一般是以下:

網(wǎng)絡(luò)請(qǐng)求

計(jì)時(shí)器

DOM時(shí)間監(jiān)聽

...

事件循環(huán)與消息隊(duì)列

回到事件循環(huán) event loop

其實(shí) 事件循環(huán) 機(jī)制和 消息隊(duì)列 的維護(hù)是由事件觸發(fā)線程控制的。

事件觸發(fā)線程 同樣是瀏覽器渲染引擎提供的,它會(huì)維護(hù)一個(gè) 消息隊(duì)列

JS引擎線程遇到異步(DOM事件監(jiān)聽、網(wǎng)絡(luò)請(qǐng)求、setTimeout計(jì)時(shí)器等...),會(huì)交給相應(yīng)的線程多帶帶去維護(hù)異步任務(wù),等待某個(gè)時(shí)機(jī)(計(jì)時(shí)器結(jié)束、網(wǎng)絡(luò)請(qǐng)求成功、用戶點(diǎn)擊DOM),然后由 事件觸發(fā)線程 將異步對(duì)應(yīng)的 回調(diào)函數(shù) 加入到消息隊(duì)列中,消息隊(duì)列中的回調(diào)函數(shù)等待被執(zhí)行。

同時(shí),JS引擎線程會(huì)維護(hù)一個(gè) 執(zhí)行棧,同步代碼會(huì)依次加入執(zhí)行棧然后執(zhí)行,結(jié)束會(huì)退出執(zhí)行棧。

如果執(zhí)行棧里的任務(wù)執(zhí)行完成,即執(zhí)行棧為空的時(shí)候(即JS引擎線程空閑),事件觸發(fā)線程才會(huì)從消息隊(duì)列取出一個(gè)任務(wù)(即異步的回調(diào)函數(shù))放入執(zhí)行棧中執(zhí)行。

消息隊(duì)列是類似隊(duì)列的數(shù)據(jù)結(jié)構(gòu),遵循先入先出(FIFO)的規(guī)則。

執(zhí)行完了后,執(zhí)行棧再次為空,事件觸發(fā)線程會(huì)重復(fù)上一步操作,再取出一個(gè)消息隊(duì)列中的任務(wù),這種機(jī)制就被稱為事件循環(huán)(event loop)機(jī)制。

還是上面的代碼:

console.log("script start")

setTimeout(() => {
  console.log("timer over")
}, 1000)

// 點(diǎn)擊頁(yè)面
console.log("click page")

console.log("script end")

// script start
// click page
// script end
// timer over

執(zhí)行過(guò)程:

主代碼塊(script)依次加入執(zhí)行棧,依次執(zhí)行,主代碼塊為:

console.log("script start")

setTimeout()

console.log("click page")

console.log("script end")

console.log() 為同步代碼,JS引擎線程處理,打印 "script start",出棧;

遇到異步函數(shù) setTimeout,交給定時(shí)器觸發(fā)線程(異步觸發(fā)函數(shù)為:setTimeout,回調(diào)函數(shù)為:() => { ... }),JS引擎線程繼續(xù),出棧;

console.log() 為同步代碼,JS引擎線程處理,打印 "click page",出棧;

console.log() 為同步代碼,JS引擎線程處理,打印 "script end",出棧;

執(zhí)行棧為空,也就是JS引擎線程空閑,這時(shí)從消息隊(duì)列中取出(如果有的話)一條任務(wù)(callback)加入執(zhí)行棧,并執(zhí)行;

重復(fù)第6步。

(此步的位置不確定)某個(gè)時(shí)刻(1000ms后),定時(shí)器觸發(fā)線程通知事件觸發(fā)線程,事件觸發(fā)線程將回調(diào)函數(shù) () => { ... } 加入消息隊(duì)列隊(duì)尾,等待JS引擎線程執(zhí)行。

可以看出,setTimeout異步函數(shù)對(duì)應(yīng)的回調(diào)函數(shù)( () => {} )會(huì)在執(zhí)行棧為空,主代碼塊執(zhí)行完了后才會(huì)執(zhí)行。

零延時(shí):

console.log("script start")

setTimeout(() => {
  console.log("timer 1 over")
}, 1000)

setTimeout(() => {
  console.log("timer 2 over")
}, 0)

console.log("script end")

// script start
// script end
// timer 2 over
// timer 1 over

這里會(huì)先打印 "timer 2 over",然后打印 "timer 1 over",盡管 timer 1 先被定時(shí)器觸發(fā)線程處理,但是 timer 2 的callback會(huì)先加入消息隊(duì)列。

上面,timer 2 的延時(shí)為 0ms,HTML5標(biāo)準(zhǔn)規(guī)定 setTimeout 第二個(gè)參數(shù)不得小于4(不同瀏覽器最小值會(huì)不一樣),不足會(huì)自動(dòng)增加,所以 "timer 2 over" 還是會(huì)在 "script end" 之后。

就算延時(shí)為 0ms,只是 timer 2 的回調(diào)函數(shù)會(huì)立即加入消息隊(duì)列而已,回調(diào)的執(zhí)行還是得等執(zhí)行棧為空(JS引擎線程空閑)時(shí)執(zhí)行。

其實(shí) setTimeout 的第二個(gè)參數(shù)并不能代表回調(diào)執(zhí)行的準(zhǔn)確的延時(shí)事件,它只能表示回調(diào)執(zhí)行的最小延時(shí)時(shí)間,因?yàn)榛卣{(diào)函數(shù)進(jìn)入消息隊(duì)列后需要等待執(zhí)行棧中的同步任務(wù)執(zhí)行完成,執(zhí)行棧為空時(shí)才會(huì)被執(zhí)行。
宏任務(wù)與微任務(wù)

以上機(jī)制在ES5的情況下夠用了,但是ES6會(huì)有一些問題。

Promise同樣是用來(lái)處理異步的:

console.log("script start")

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

Promise.resolve().then(function() {
    console.log("promise1")
}).then(function() {
    console.log("promise2")
})

console.log("script end")

// script start
// script end
// promise1
// promise2
// timer over

WTF?? "promise 1" "promise 2" 在 "timer over" 之前打印了?

這里有一個(gè)新概念:macrotask(宏任務(wù)) 和 microtask(微任務(wù))。

所有任務(wù)分為 macrotaskmicrotask:

macrotask:主代碼塊、setTimeout、setInterval等(可以看到,事件隊(duì)列中的每一個(gè)事件都是一個(gè) macrotask,現(xiàn)在稱之為宏任務(wù)隊(duì)列)

microtask:Promise、process.nextTick等

JS引擎線程首先執(zhí)行主代碼塊。

每次執(zhí)行棧執(zhí)行的代碼就是一個(gè)宏任務(wù),包括任務(wù)隊(duì)列(宏任務(wù)隊(duì)列)中的,因?yàn)閳?zhí)行棧中的宏任務(wù)執(zhí)行完會(huì)去取任務(wù)隊(duì)列(宏任務(wù)隊(duì)列)中的任務(wù)加入執(zhí)行棧中,即同樣是事件循環(huán)的機(jī)制。

在執(zhí)行宏任務(wù)時(shí)遇到Promise等,會(huì)創(chuàng)建微任務(wù)(.then()里面的回調(diào)),并加入到微任務(wù)隊(duì)列隊(duì)尾。

microtask必然是在某個(gè)宏任務(wù)執(zhí)行的時(shí)候創(chuàng)建的,而在下一個(gè)宏任務(wù)開始之前,瀏覽器會(huì)對(duì)頁(yè)面重新渲染(task >> 渲染 >> 下一個(gè)task(從任務(wù)隊(duì)列中取一個(gè)))。同時(shí),在上一個(gè)宏任務(wù)執(zhí)行完成后,渲染頁(yè)面之前,會(huì)執(zhí)行當(dāng)前微任務(wù)隊(duì)列中的所有微任務(wù)。

也就是說(shuō),在某一個(gè)macrotask執(zhí)行完后,在重新渲染與開始下一個(gè)宏任務(wù)之前,就會(huì)將在它執(zhí)行期間產(chǎn)生的所有microtask都執(zhí)行完畢(在渲染前)。

這樣就可以解釋 "promise 1" "promise 2" 在 "timer over" 之前打印了。"promise 1" "promise 2" 做為微任務(wù)加入到微任務(wù)隊(duì)列中,而 "timer over" 做為宏任務(wù)加入到宏任務(wù)隊(duì)列中,它們同時(shí)在等待被執(zhí)行,但是微任務(wù)隊(duì)列中的所有微任務(wù)都會(huì)在開始下一個(gè)宏任務(wù)之前都被執(zhí)行完。

在node環(huán)境下,process.nextTick的優(yōu)先級(jí)高于Promise,也就是說(shuō):在宏任務(wù)結(jié)束后會(huì)先執(zhí)行微任務(wù)隊(duì)列中的nextTickQueue,然后才會(huì)執(zhí)行微任務(wù)中的Promise。

執(zhí)行機(jī)制:

執(zhí)行一個(gè)宏任務(wù)(棧中沒有就從事件隊(duì)列中獲取)

執(zhí)行過(guò)程中如果遇到微任務(wù),就將它添加到微任務(wù)的任務(wù)隊(duì)列中

宏任務(wù)執(zhí)行完畢后,立即執(zhí)行當(dāng)前微任務(wù)隊(duì)列中的所有微任務(wù)(依次執(zhí)行)

當(dāng)前宏任務(wù)執(zhí)行完畢,開始檢查渲染,然后GUI線程接管渲染

渲染完畢后,JS引擎線程繼續(xù),開始下一個(gè)宏任務(wù)(從宏任務(wù)隊(duì)列中獲取)

總結(jié)

JavaScript 是單線程語(yǔ)言,決定于它的設(shè)計(jì)最初是用來(lái)處理瀏覽器網(wǎng)頁(yè)的交互。瀏覽器負(fù)責(zé)解釋和執(zhí)行 JavaScript 的線程只有一個(gè)(所有說(shuō)是單線程),即JS引擎線程,但是瀏覽器同樣提供其他線程,如:事件觸發(fā)線程、定時(shí)器觸發(fā)線程等。

異步一般是指:

網(wǎng)絡(luò)請(qǐng)求

計(jì)時(shí)器

DOM事件監(jiān)聽

事件循環(huán)機(jī)制:

JS引擎線程會(huì)維護(hù)一個(gè)執(zhí)行棧,同步代碼會(huì)依次加入到執(zhí)行棧中依次執(zhí)行并出棧。

JS引擎線程遇到異步函數(shù),會(huì)將異步函數(shù)交給相應(yīng)的Webapi,而繼續(xù)執(zhí)行后面的任務(wù)。

Webapi會(huì)在條件滿足的時(shí)候,將異步對(duì)應(yīng)的回調(diào)加入到消息隊(duì)列中,等待執(zhí)行。

執(zhí)行棧為空時(shí),JS引擎線程會(huì)去取消息隊(duì)列中的回調(diào)函數(shù)(如果有的話),并加入到執(zhí)行棧中執(zhí)行。

完成后出棧,執(zhí)行棧再次為空,重復(fù)上面的操作,這就是事件循環(huán)(event loop)機(jī)制。

原文鏈接

參考:

Tasks, microtasks, queues and schedules

great talk at JSConf on the event loop

并發(fā)模型與事件循環(huán) - JavaScript | MDN

從瀏覽器多進(jìn)程到JS單線程,JS運(yùn)行... - 掘金

瀏覽器篇-Event-Loop.md - PDKSophia/blog.io

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/99143.html

相關(guān)文章

  • 深入淺出JavaScript運(yùn)行機(jī)制

    摘要:主線程從任務(wù)隊(duì)列中讀取事件,這個(gè)過(guò)程是循環(huán)不斷的,所以整個(gè)的這種運(yùn)行機(jī)制又稱為事件循環(huán)。上面也提到,在到達(dá)指定時(shí)間時(shí),定時(shí)器就會(huì)將相應(yīng)回調(diào)函數(shù)插入任務(wù)隊(duì)列尾部。這就是定時(shí)器功能。關(guān)于定時(shí)器的重要補(bǔ)充定時(shí)器包括與兩個(gè)方法。 一、引子 本文介紹JavaScript運(yùn)行機(jī)制,這一部分比較抽象,我們先從一道面試題入手: console.log(1); setTimeout(function()...

    mochixuan 評(píng)論0 收藏0
  • 深入淺出JavaScript運(yùn)行機(jī)制

    摘要:主線程從任務(wù)隊(duì)列中讀取事件,這個(gè)過(guò)程是循環(huán)不斷的,所以整個(gè)的這種運(yùn)行機(jī)制又稱為事件循環(huán)。上面也提到,在到達(dá)指定時(shí)間時(shí),定時(shí)器就會(huì)將相應(yīng)回調(diào)函數(shù)插入任務(wù)隊(duì)列尾部。這就是定時(shí)器功能。關(guān)于定時(shí)器的重要補(bǔ)充定時(shí)器包括與兩個(gè)方法。 一、引子 本文介紹JavaScript運(yùn)行機(jī)制,這一部分比較抽象,我們先從一道面試題入手: console.log(1); setTimeout(function()...

    魏明 評(píng)論0 收藏0
  • 深入淺出JavaScript運(yùn)行機(jī)制

    摘要:主線程從任務(wù)隊(duì)列中讀取事件,這個(gè)過(guò)程是循環(huán)不斷的,所以整個(gè)的這種運(yùn)行機(jī)制又稱為事件循環(huán)。上面也提到,在到達(dá)指定時(shí)間時(shí),定時(shí)器就會(huì)將相應(yīng)回調(diào)函數(shù)插入任務(wù)隊(duì)列尾部。這就是定時(shí)器功能。關(guān)于定時(shí)器的重要補(bǔ)充定時(shí)器包括與兩個(gè)方法。 一、引子 本文介紹JavaScript運(yùn)行機(jī)制,這一部分比較抽象,我們先從一道面試題入手: console.log(1); setTimeout(function()...

    chaosx110 評(píng)論0 收藏0
  • JavaScript 異步編程

    摘要:下面我將介紹的基本用法以及如何在異步編程中使用它們。在沒有發(fā)布之前,作為異步編程主力軍的回調(diào)函數(shù)一直被人詬病,其原因有太多比如回調(diào)地獄代碼執(zhí)行順序難以追蹤后期因代碼變得十分復(fù)雜導(dǎo)致無(wú)法維護(hù)和更新等,而的出現(xiàn)在很大程度上改變了之前的窘境。 前言 自己著手準(zhǔn)備寫這篇文章的初衷是覺得如果想要更深入的理解 JS,異步編程則是必須要跨過(guò)的一道坎。由于這里面涉及到的東西很多也很廣,在初學(xué) JS 的...

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

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

0條評(píng)論

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