談談 Event Loop(事件循環)機制

在講 Event Loop (事件循環)以前,咱們來了解點 node 的東西,來幫助咱們更加明白事件循環是幹什麼的vue

Node 是什麼

Node.js 是一個基於 Chrome V8 引擎的 JavaScript 運行環境,Node 不是一門語言,是讓 js 運行在後端的,運行時不包括 js 全集,由於在服務端中不包含 DOM 和 BOM,Node 也提供了一些新的模塊,好比 http,fs等模塊。java

Node 解決了什麼

Node 的首要目標是提供一種簡單的,用於建立高性能服務器的開發工具 Web 服務器的瓶頸在於併發的用戶量,對比 Java 和 Php 的實現方式node

Node在處理高併發,I/O 密集場景有明顯的性能優點
  • 高併發,是指在同一時間併發訪問服務器
  • I/O 密集指的是文件操做、網絡操做、數據庫,相對的有 CPU 密集,CPU 密集指的是邏輯處理運算、壓縮、解壓、加密、解密

Web 主要場景就是接收客戶端的請求讀取靜態資源和渲染界面,因此 Node 很是適合 Web 應用的開發。web

進程與線程

進程是操做系統分配資源和調度任務的基本單位,線程是創建在進程上的一次程序運行單位,一個進程上能夠有多個線程。ajax

  1. 瀏覽器線程
    • 用戶界面-包括地址欄、前進/後退按鈕、書籤菜單等
    • 瀏覽器引擎-在用戶界面和呈現引擎之間傳送指令(瀏覽器的主進程)
    • 渲染引擎,也被稱爲瀏覽器內核(瀏覽器渲染進程)
    • 一個插件對應一個進程(第三方插件進程)
    • GPU提升網頁瀏覽的體驗( GPU 進程)
  2. 瀏覽器渲染引擎
    • 渲染引擎內部是多線程的,內部包含 ui 線程和 js 線程
    • js 線程 ui 線程 這兩個線程互斥的,目的就是爲了保證不產生衝突。
    • ui 線程會把更改的放到隊列中,當 js 線程空閒下來的時候,ui 線程在繼續渲染
  3. js 單線程
    • js 是單線程,爲何呢?若是多個線程同時操做 DOM ,哪頁面不會很混亂?這裏所謂的單線程指的是主線程是單線程的,因此在 Node 中主線程依舊是單線程的。
  4. webworker 多線程
    • 它和 js 主線程不是平級的,主線程能夠控制 webworker,可是 webworker不能操做 DOM,不能獲取 document,window
  5. 其餘線程
    • 瀏覽器事件觸發線程(用來控制事件循環,存放 setTimeout、瀏覽器事件、ajax 的回調函數)
    • 定時觸發器線程(setTimeout 定時器所在線程)
    • 異步 HTTP 請求線程(ajax 請求線程)

單線程特色是節約了內存,而且不須要在切換執行上下文。並且單線程不須要管鎖的問題,所謂 鎖,在 java 裏纔有鎖的概念,因此咱們不用細研究數據庫

瀏覽器中的 Event Loop

  • 同步任務都在主線程上執行,造成一個執行棧
  • 主線程以外,還存在一個任務隊列。只要異步任務有了運行結果,就在任務隊列之中放置一個事件。
  • 一旦執行棧中的全部同步任務執行完畢,系統就會讀取任務隊列,將隊列中的事件放到執行棧中依次執行
  • 主線程從任務隊列中讀取事件,這個過程是循環不斷的

整個的這種運行機制又稱爲 Event Loop (事件循環)後端

node 中的 Event Loop

如圖(圖片是借鑑的):promise

* 咱們寫的代碼會交給 V8 引擎去進行處理 * 代碼中可能會調用 nodeApi,node 會交給 LIBUV 庫處理 * LIBUV 經過阻塞 i/o 和多線程實現了異步 io * 經過事件驅動的方式,將結果放到事件隊列中,最終交給咱們的應用。

同步,異步 阻塞和非阻塞

  • 阻塞和非阻塞指的是調用者的狀態,關注的是程序在等待調用結果時的狀態
  • 同步和異步指的是被調用者是如何通知的,關注的是消息通知機制

宏任務和微任務

  • macro-task(宏任務):
    • setTimeout, setInterval, setImmediate, I/O
  • micro-task(微任務):
    • process.nextTick,
    • 原生 Promise (有些實現的promise 將 then 方法放到了宏任務中,瀏覽器默認放到了微任務),
    • Object.observe (已廢棄),
    • MutationObserver(不兼容,已廢棄)
    • MessageChannel(vue中 nextTick 實現原理)

同步代碼先執行,執行是在棧中執行的,微任務大於宏任務,微任務會先執行(棧),宏任務後執行(隊列)瀏覽器

講到這裏,敲幾行代碼來總結下咱們上面講到的知識點把bash

《1》宏任務,微任務在瀏覽器和 node 環境執行順序不一樣

// 這個列子裏面,包含了宏任務,微任務,分別看看瀏覽器和node 打印的結果
console.log(1)
// 棧
setTimeout(function(){
    console.log(2)
    // 微任務
    Promise.resolve(100).then(function(){
        console.log('promise')
    })
})
// 棧
let promise = new Promise(function(resolve, reject){
    console.log(7)
    resolve(100)
}).then(function(data){
    // 微任務
    console.log(data)
})
// 棧
setTimeout(function(){
    console.log(3)
})
console.log(5)
// 瀏覽器結果:1 7 5 100 2 promise 3
// node 結果:  1 7 5 100 2 3 promise
複製代碼

瀏覽器和 node 環境執行順序不一樣,瀏覽器是先把一個棧以及棧中的微任務走完,纔會走下一個棧。node 環境裏面是把因此棧走完,才走微任務

《2》setTimeout setImmediate 都是宏任務,誰優先執行呢?

setTimeout(function(){
    console.log('timeout')
})
setImmediate(function(){
    console.log('setImmediate')
})
// 結果打印:timeout setImmediate
複製代碼

setTimeout setImmediate 這兩個取決於 node 的執行時間

《3》nextTick 和 then 都屬於微任務,誰優先執行呢?

process.nextTick(function(){
    console.log('nextTick')
})
Promise.resolve().then(function(){
    console.log('then')
})
// 結果打印:nextTick then

// 再加一個宏任務呢
setImmediate(function(){
    console.log('setImmediate')
})
// 結果打印:nextTick  then  setImmediate
複製代碼

nextTick 會比 其餘微任務、宏任務執行快

《4》i/o 文件操做(宏任務),搭配微任務,誰優先執行呢?

let fs = require('fs');
fs.readFile('./1/log',function(){
    console.log('fs')
})
process.nextTick(function(){
    console.log('text')
})
// 結果打印:text  fs
複製代碼

i/o 文件操做(宏任務), 若是有微任務,先執行微任務,在執行文件讀取

相關文章
相關標籤/搜索