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

資訊專欄INFORMATION COLUMN

瀏覽器中的事件循環機制

zzbo / 583人閱讀

摘要:單線程的話,如果我們做一些的操作比如說這是一個耗時的操所那么在這將近一秒內,線程就會被阻塞,無法繼續執行下面的任務。事件循環的主要機制就是任務隊列機制一個事件循環有一個或者多個任務隊列。

瀏覽器中的事件循環機制

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

正文

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

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

那么在這將近一秒內,線程就會被阻塞,無法繼續執行下面的任務。

還有些操作比如說獲取遠程數據、I/O操作等,他們都很耗時,如果采用同步的方式,那么進程在執行這些操作時就會因為耗時而等待,就像上面那樣,下面的任務也只能等待,這樣效率并不高。

那瀏覽器是怎么做的呢?

我們找到WHATWG規范對Event loop的介紹:

為了協調事件,用戶交互,腳本,渲染,網絡等,用戶代理必須使用事件循環。

事件循環的主要機制就是任務隊列機制:

一個事件循環有一個或者多個任務隊列(task queues)。任務隊列是task的有序列表,task是調度Events,Parsing,Callbacks,Using a resource,Reacting to DOM manipulation這些任務的算法;

每個任務都來自一個特定的任務源(task source)(比如鼠標鍵盤事件)。來自同一個特定任務源且屬于特定事件循環的任務必須被加入到同一個任務隊列中,來自不同任務源的任務可以放在不同的任務隊列中;

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

在調用任務的過程中, 會產生新的任務, 瀏覽器就會不斷執行任務, 因此稱為事件循環.

microtask queue 微任務隊列

還有一些特殊任務, 它們不會被放在task queues中, 會放在一個叫做microtask(微任務) queue中, 繼續看標準:

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.

任務隊列可以有多個, 但是微任務隊列只有一個.

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

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

microtask(微任務): process.nextTick, Promises(這里指瀏覽器實現的原生 Promise), Object.observe, MutationObserver

請尤其注意macrotask中執行整體代碼也是一個宏任務

事件循環處理過程

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

從macrotask隊列中(task queue)取一個宏任務執行, 執行完后, 取出所有的microtask執行.

重復回合

無論在執行macrotask還是microtask, 都有可能產生新的macrotask或者microtask, 就這樣繼續執行.

用任務隊列機制解釋異步操作順序

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

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")
})

結果(Chrome 63.0.3239.84; Mac OS):

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

這個順序是如何得來的?

我們先講promise 4后面只出現一次setInterval的情況, 畫個圖簡單表示一下這個過程:

注意

本圖為了方便把各時間段(Cycle)隊列的任務都畫在隊列中去了, 實際上執行一個task 和 microtask 后就會把這個任務從相應隊列中刪除

首先, 主任務就是執行腳本, 也就是執行上述代碼, 這也是一個task. 在執行代碼過程中, 遇到setTimeout、setInterval 就會將回調函數添加到task queue中, 遇到 promise 就會將then回調添加到 microtask 中去.

Task執行完, 接著取所有 microtask 執行, 所有microtask 執行完了, microtask queue也就空了, 接著再取task執行, 如果microtask queue為空, 沒有任務, 則繼續取下一個task執行, 就這樣循環執行. 圖中箭頭就表示執行的順序.

那么為什么promise 4后面大部分情況下出現2次setInterval, 少數情況出現1次呢?

我猜測這是因為setInterval是有最短間隔時間的(chrome下4ms左右), 這個時間不同機子、不同瀏覽器都有可能不一樣. 代碼中的參數是0, 意味著盡可能短的時間內就會產生一個task加入到 task queue中. 瀏覽器在執行setInterval后到執行下一個task前, 時間間隔就可能超過這個最短時間, 因此會產生一個setInterval task.

我是這樣論證的:

我把含有promise5、promise6回調函數的setTimeout的時間設置大一點, 讓它推遲插入task queue中:

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

結果是promise 4后面的setInterval出現了5次, 因此我覺得promise 4后面大部分情況下出現2次setInterval、少數情況出現一次的原因就是瀏覽器在執行setInterval回調函數后、執行setTimeout回調函數前, 時間間隔大部分情況超過了這個最短時間.

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

Node中的奇怪結果

首先說明一下, 在Node中也體現了任務隊列的機制, 但是這不是Node實現的, 這是V8實現的, 由Node調用了V8任務隊列機制的API. 至于為什么是V8實現的, 我們翻翻ECMA 262 標準對 Job 和 Job queue 的介紹就可以得知

但是讓人摸不著頭腦的是, 這段代碼在node v8.5.0下有時會出現這樣的結果:

promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setTimeout 2
setInterval   // 為什么會出現setInterval???
promise 5
promise 6

按理說應該是setTimeout 2 => promise 5 => promise 6, 因為輸出setTimeout 2的回調函數是task, 執行完這個task后應該調用microtask 輸出promise 5 => promise 6啊? 很奇怪! Node對V8確實有些改動, 不知道是不是這方面原因...

還請大神解惑!

你竟然讀到這了

總結一下:

學習技術還是有捷徑的, 那就是讀標準 ;)

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/90751.html

相關文章

  • JavaScript運行機制事件循環

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

    Ververica 評論0 收藏0
  • 事件循環機制(event loop)

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

    tain335 評論0 收藏0
  • Js 的事件循環(Event Loop)機制以及實例講解

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

    Anshiii 評論0 收藏0
  • JS覽器事件循環機制

    摘要:事件循環機制事件循環機制分為瀏覽器和事件循環機制,兩者的實現技術不一樣,瀏覽器是中定義的規范,是由庫實現。整個事件循環完成之后,會去檢測微任務的任務隊列中是否存在任務,存在就執行。 文章來自我的 github 博客,包括技術輸出和學習筆記,歡迎star。 先來明白些概念性內容。 進程、線程 進程是系統分配的獨立資源,是 CPU 資源分配的基本單位,進程是由一個或者多個線程組成的。 線...

    zebrayoung 評論0 收藏0
  • JS JavaScript事件循環機制

    摘要:事件循環機制首先區分進程和線程進程是資源分配的最小單位系統會給它分配內存不同的進程之間是可以同學的,如管道命名管道消息隊列一個進程里有單個或多個線程瀏覽器是多進程的,因為系統給它的進程分配了資源內存打開會有一個主進程,每打開一個頁就有一個獨 JS JavaScript事件循環機制 首先區分進程和線程 進程是cpu資源分配的最小單位(系統會給它分配內存) 不同的進程之間是可以同學的,如...

    dantezhao 評論0 收藏0

發表評論

0條評論

zzbo

|高級講師

TA的文章

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