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

資訊專欄INFORMATION COLUMN

瀏覽器中的事件循環(huán)機(jī)制

zzbo / 593人閱讀

摘要:?jiǎn)尉€程的話,如果我們做一些的操作比如說這是一個(gè)耗時(shí)的操所那么在這將近一秒內(nèi),線程就會(huì)被阻塞,無法繼續(xù)執(zhí)行下面的任務(wù)。事件循環(huán)的主要機(jī)制就是任務(wù)隊(duì)列機(jī)制一個(gè)事件循環(huán)有一個(gè)或者多個(gè)任務(wù)隊(duì)列。

瀏覽器中的事件循環(huán)機(jī)制

網(wǎng)上一搜事件循環(huán), 很多文章標(biāo)題的前面會(huì)加上 JavaScript, 但是我覺得事件循環(huán)機(jī)制跟 JavaScript 沒什么關(guān)系, JavaScript 只是一門解釋型語言, 方便開發(fā)和理解的, 由V8 JIT將 JavaScript 編譯成機(jī)器語言來調(diào)用底層, 至于瀏覽器怎么執(zhí)行 JavaScript 代碼, JavaScript 管不著也不關(guān)心. 因此, “JavaScript事件循環(huán)機(jī)制”這種說法是不合理的. 事件循環(huán)機(jī)制是由運(yùn)行時(shí)環(huán)境實(shí)現(xiàn)的, 具體來說有瀏覽器、Node等. 這篇文章就先來說說瀏覽器中實(shí)現(xiàn)的事件循環(huán)機(jī)制.

正文

首先,javascript 在瀏覽器端運(yùn)行是單線程的,這是由瀏覽器決定的,這是為了避免多線程執(zhí)行不同任務(wù)會(huì)發(fā)生沖突的情況。也就是說我們寫的javascript 代碼只在一個(gè)線程上運(yùn)行,稱之為主線程(HTML5提供了web worker API可以讓瀏覽器開一個(gè)線程運(yùn)行比較復(fù)雜耗時(shí)的 javascript任務(wù),但是這個(gè)線程仍受主線程的控制)。單線程的話,如果我們做一些“sleep”的操作比如說:

var now = + new Date()
while (+new Date() <= now + 1000){
//這是一個(gè)耗時(shí)的操所
}

那么在這將近一秒內(nèi),線程就會(huì)被阻塞,無法繼續(xù)執(zhí)行下面的任務(wù)。

還有些操作比如說獲取遠(yuǎn)程數(shù)據(jù)、I/O操作等,他們都很耗時(shí),如果采用同步的方式,那么進(jìn)程在執(zhí)行這些操作時(shí)就會(huì)因?yàn)楹臅r(shí)而等待,就像上面那樣,下面的任務(wù)也只能等待,這樣效率并不高。

那瀏覽器是怎么做的呢?

我們找到WHATWG規(guī)范對(duì)Event loop的介紹:

為了協(xié)調(diào)事件,用戶交互,腳本,渲染,網(wǎng)絡(luò)等,用戶代理必須使用事件循環(huán)。

事件循環(huán)的主要機(jī)制就是任務(wù)隊(duì)列機(jī)制:

一個(gè)事件循環(huán)有一個(gè)或者多個(gè)任務(wù)隊(duì)列(task queues)。任務(wù)隊(duì)列是task的有序列表,task是調(diào)度Events,Parsing,Callbacks,Using a resource,Reacting to DOM manipulation這些任務(wù)的算法;

每個(gè)任務(wù)都來自一個(gè)特定的任務(wù)源(task source)(比如鼠標(biāo)鍵盤事件)。來自同一個(gè)特定任務(wù)源且屬于特定事件循環(huán)的任務(wù)必須被加入到同一個(gè)任務(wù)隊(duì)列中,來自不同任務(wù)源的任務(wù)可以放在不同的任務(wù)隊(duì)列中;

瀏覽器調(diào)用這些隊(duì)列中的任務(wù)時(shí)采取這樣的做法: 相同隊(duì)列中的任務(wù)按照先進(jìn)先出的順序, 不同的隊(duì)列按照提前設(shè)置的隊(duì)列優(yōu)先級(jí)來調(diào)用. 例如,用戶代理可以有一個(gè)用于鼠標(biāo)和鍵盤事件的任務(wù)隊(duì)列(用戶交互任務(wù)源),另一個(gè)用于其他任務(wù)。然后,用戶代理75%概率調(diào)用鍵盤和鼠標(biāo)事件任務(wù)隊(duì)列,25%調(diào)用其他隊(duì)列, 這樣的話就保持界面響應(yīng)而且不會(huì)餓死其他任務(wù)隊(duì)列. 但是相同隊(duì)列中的任務(wù)要按照先進(jìn)先出的順序。也就是說多帶帶的任務(wù)隊(duì)列中的任務(wù)總是按先進(jìn)先出的順序執(zhí)行,但是不保證多個(gè)任務(wù)隊(duì)列中的任務(wù)優(yōu)先級(jí),具體實(shí)現(xiàn)可能會(huì)交叉執(zhí)行

在調(diào)用任務(wù)的過程中, 會(huì)產(chǎn)生新的任務(wù), 瀏覽器就會(huì)不斷執(zhí)行任務(wù), 因此稱為事件循環(huán).

microtask queue 微任務(wù)隊(duì)列

還有一些特殊任務(wù), 它們不會(huì)被放在task queues中, 會(huì)放在一個(gè)叫做microtask(微任務(wù)) queue中, 繼續(xù)看標(biāo)準(zhǔn):

Each event loop has a microtask queue. A microtask is a task that is originally to be queued on the microtask queue rather than a task queue.

任務(wù)隊(duì)列可以有多個(gè), 但是微任務(wù)隊(duì)列只有一個(gè).

那么哪些任務(wù)是放在task queue, 哪些放在microtask queue呢? 通常對(duì)瀏覽器和Node.js來說:

macrotask(宏任務(wù)): script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering等

microtask(微任務(wù)): process.nextTick, Promises(這里指瀏覽器實(shí)現(xiàn)的原生 Promise), Object.observe, MutationObserver

請(qǐng)尤其注意macrotask中執(zhí)行整體代碼也是一個(gè)宏任務(wù)

事件循環(huán)處理過程

總體來說, 瀏覽器端事件循環(huán)的一個(gè)回合(go-around或者叫cycle)就是:

從macrotask隊(duì)列中(task queue)取一個(gè)宏任務(wù)執(zhí)行, 執(zhí)行完后, 取出所有的microtask執(zhí)行.

重復(fù)回合

無論在執(zhí)行macrotask還是microtask, 都有可能產(chǎn)生新的macrotask或者microtask, 就這樣繼續(xù)執(zhí)行.

用任務(wù)隊(duì)列機(jī)制解釋異步操作順序

這里有一些常見異步操作:

const interval = setInterval(() => {
  console.log("setInterval")
}, 0)

setTimeout(() => {  
  console.log("setTimeout 1")
  Promise.resolve().then(() => {
    console.log("promise 3")
  }).then(() => {
    console.log("promise 4")
  }).then(() => {
    setTimeout(() => {
      console.log("setTimeout 2")
      Promise.resolve().then(() => {
        console.log("promise 5")
      }).then(() => {
        console.log("promise 6")
      }).then(() => {
          clearInterval(interval)
      })
    }, 0)
  })
}, 0)

Promise.resolve().then(() => {
  console.log("promise 1")
}).then(() => {
  console.log("promise 2")
})

結(jié)果(Chrome 63.0.3239.84; Mac OS):

promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval // 大部分情況下2次, 少數(shù)情況下一次
setTimeout 2
promise 5
promise 6

這個(gè)順序是如何得來的?

我們先講promise 4后面只出現(xiàn)一次setInterval的情況, 畫個(gè)圖簡(jiǎn)單表示一下這個(gè)過程:

注意

本圖為了方便把各時(shí)間段(Cycle)隊(duì)列的任務(wù)都畫在隊(duì)列中去了, 實(shí)際上執(zhí)行一個(gè)task 和 microtask 后就會(huì)把這個(gè)任務(wù)從相應(yīng)隊(duì)列中刪除

首先, 主任務(wù)就是執(zhí)行腳本, 也就是執(zhí)行上述代碼, 這也是一個(gè)task. 在執(zhí)行代碼過程中, 遇到setTimeout、setInterval 就會(huì)將回調(diào)函數(shù)添加到task queue中, 遇到 promise 就會(huì)將then回調(diào)添加到 microtask 中去.

Task執(zhí)行完, 接著取所有 microtask 執(zhí)行, 所有microtask 執(zhí)行完了, microtask queue也就空了, 接著再取task執(zhí)行, 如果microtask queue為空, 沒有任務(wù), 則繼續(xù)取下一個(gè)task執(zhí)行, 就這樣循環(huán)執(zhí)行. 圖中箭頭就表示執(zhí)行的順序.

那么為什么promise 4后面大部分情況下出現(xiàn)2次setInterval, 少數(shù)情況出現(xiàn)1次呢?

我猜測(cè)這是因?yàn)閟etInterval是有最短間隔時(shí)間的(chrome下4ms左右), 這個(gè)時(shí)間不同機(jī)子、不同瀏覽器都有可能不一樣. 代碼中的參數(shù)是0, 意味著盡可能短的時(shí)間內(nèi)就會(huì)產(chǎn)生一個(gè)task加入到 task queue中. 瀏覽器在執(zhí)行setInterval后到執(zhí)行下一個(gè)task前, 時(shí)間間隔就可能超過這個(gè)最短時(shí)間, 因此會(huì)產(chǎn)生一個(gè)setInterval task.

我是這樣論證的:

我把含有promise5、promise6回調(diào)函數(shù)的setTimeout的時(shí)間設(shè)置大一點(diǎn), 讓它推遲插入task queue中:

...  
setTimeout(() => {
      console.log("setTimeout 2")
      Promise.resolve().then(() => {
        console.log("promise 5")
      }).then(() => {
        console.log("promise 6")
      }).then(() => {
          clearInterval(interval)
      })
}, 10)   //這里加上10ms 
...

結(jié)果是promise 4后面的setInterval出現(xiàn)了5次, 因此我覺得promise 4后面大部分情況下出現(xiàn)2次setInterval、少數(shù)情況出現(xiàn)一次的原因就是瀏覽器在執(zhí)行setInterval回調(diào)函數(shù)后、執(zhí)行setTimeout回調(diào)函數(shù)前, 時(shí)間間隔大部分情況超過了這個(gè)最短時(shí)間.

另外, 我試著再依次加上1ms, 直到14ms——也就是加上4ms時(shí), promise 4后面的setInterval變成了6次, 可以認(rèn)為setInterval最短間隔時(shí)間在Chrome下約為4ms(不考慮機(jī)子性能、設(shè)置).

Node中的奇怪結(jié)果

首先說明一下, 在Node中也體現(xiàn)了任務(wù)隊(duì)列的機(jī)制, 但是這不是Node實(shí)現(xiàn)的, 這是V8實(shí)現(xiàn)的, 由Node調(diào)用了V8任務(wù)隊(duì)列機(jī)制的API. 至于為什么是V8實(shí)現(xiàn)的, 我們翻翻ECMA 262 標(biāo)準(zhǔn)對(duì) Job 和 Job queue 的介紹就可以得知

但是讓人摸不著頭腦的是, 這段代碼在node v8.5.0下有時(shí)會(huì)出現(xiàn)這樣的結(jié)果:

promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setTimeout 2
setInterval   // 為什么會(huì)出現(xiàn)setInterval???
promise 5
promise 6

按理說應(yīng)該是setTimeout 2 => promise 5 => promise 6, 因?yàn)檩敵鰏etTimeout 2的回調(diào)函數(shù)是task, 執(zhí)行完這個(gè)task后應(yīng)該調(diào)用microtask 輸出promise 5 => promise 6啊? 很奇怪! Node對(duì)V8確實(shí)有些改動(dòng), 不知道是不是這方面原因...

還請(qǐng)大神解惑!

你竟然讀到這了

總結(jié)一下:

學(xué)習(xí)技術(shù)還是有捷徑的, 那就是讀標(biāo)準(zhǔn) ;)

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

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

相關(guān)文章

  • JavaScript運(yùn)行機(jī)制事件循環(huán)

    摘要:主線程不斷重復(fù)上面的三步,此過程也就是常說的事件循環(huán)。所以主線程代碼執(zhí)行時(shí)間過長(zhǎng),會(huì)阻塞事件循環(huán)的執(zhí)行。參考資料這一次,徹底弄懂執(zhí)行機(jī)制任務(wù)隊(duì)列的順序機(jī)制事件循環(huán)搞懂異步事件輪詢與中的事件循環(huán) 1. 說明 讀過本文章后,您能知道: JavaScript代碼在瀏覽器中的執(zhí)行機(jī)制和事件循環(huán) 面試中經(jīng)常遇到的代碼輸出順序問題 首先通過一段代碼來驗(yàn)證你是否了解代碼輸出順序,如果你不知道輸出...

    Ververica 評(píng)論0 收藏0
  • 事件循環(huán)機(jī)制(event loop)

    摘要:了解事件循環(huán)機(jī)制有助于理解的執(zhí)行過程,同時(shí)這也是面試常見題。那么這個(gè)回調(diào)函數(shù)將在何時(shí)由誰執(zhí)行呢已知是瀏覽器環(huán)境提供的,因此瀏覽器將對(duì)它進(jìn)行處理,瀏覽器會(huì)在本次事件完成,即計(jì)時(shí)結(jié)束后,將回調(diào)函數(shù)加入循環(huán)隊(duì)列中,然后等待被加入執(zhí)行棧執(zhí)行。 如果有人問JavaScript是什么,也許你會(huì)說它是一個(gè)單線程、非阻塞、異步、解釋型的腳本語言。那么作為一個(gè)單線程語言,它是怎么實(shí)現(xiàn)非阻塞、異步的?這就...

    tain335 評(píng)論0 收藏0
  • Js 的事件循環(huán)(Event Loop)機(jī)制以及實(shí)例講解

    摘要:主線程要明確的一點(diǎn)是,主線程跟執(zhí)行棧是不同概念,主線程規(guī)定現(xiàn)在執(zhí)行執(zhí)行棧中的哪個(gè)事件。主線程循環(huán)即主線程會(huì)不停的從執(zhí)行棧中讀取事件,會(huì)執(zhí)行完所有棧中的同步代碼。以上參考資料詳解中的事件循環(huán)機(jī)制中的事件循環(huán)運(yùn)行機(jī)制詳解再談 showImg(https://segmentfault.com/img/remote/1460000015317437?w=1920&h=1080); 前言 大家都...

    Anshiii 評(píng)論0 收藏0
  • JS覽器事件循環(huán)機(jī)制

    摘要:事件循環(huán)機(jī)制事件循環(huán)機(jī)制分為瀏覽器和事件循環(huán)機(jī)制,兩者的實(shí)現(xiàn)技術(shù)不一樣,瀏覽器是中定義的規(guī)范,是由庫(kù)實(shí)現(xiàn)。整個(gè)事件循環(huán)完成之后,會(huì)去檢測(cè)微任務(wù)的任務(wù)隊(duì)列中是否存在任務(wù),存在就執(zhí)行。 文章來自我的 github 博客,包括技術(shù)輸出和學(xué)習(xí)筆記,歡迎star。 先來明白些概念性內(nèi)容。 進(jìn)程、線程 進(jìn)程是系統(tǒng)分配的獨(dú)立資源,是 CPU 資源分配的基本單位,進(jìn)程是由一個(gè)或者多個(gè)線程組成的。 線...

    zebrayoung 評(píng)論0 收藏0
  • JS JavaScript事件循環(huán)機(jī)制

    摘要:事件循環(huán)機(jī)制首先區(qū)分進(jìn)程和線程進(jìn)程是資源分配的最小單位系統(tǒng)會(huì)給它分配內(nèi)存不同的進(jìn)程之間是可以同學(xué)的,如管道命名管道消息隊(duì)列一個(gè)進(jìn)程里有單個(gè)或多個(gè)線程瀏覽器是多進(jìn)程的,因?yàn)橄到y(tǒng)給它的進(jìn)程分配了資源內(nèi)存打開會(huì)有一個(gè)主進(jìn)程,每打開一個(gè)頁就有一個(gè)獨(dú) JS JavaScript事件循環(huán)機(jī)制 首先區(qū)分進(jìn)程和線程 進(jìn)程是cpu資源分配的最小單位(系統(tǒng)會(huì)給它分配內(nèi)存) 不同的進(jìn)程之間是可以同學(xué)的,如...

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

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

0條評(píng)論

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