談談Node中的常見概念

在學習Node的過程當中,咱們要了解一些基礎概念。什麼是Node他又解決了哪些問題以及Node的特色。包括傻傻分不清的進程和線程,同步與異步,阻塞和非阻塞,以及Node中核心的(Event Loop)事件環的概念。javascript

1.Node是什麼?

Node.js是一個基於 Chrome V8 引擎的JavaScript運行環境(runtime),Node不是一門語言是讓js運行在後端的運行時,而且不包括javascript全集,由於在服務端中不包含DOMBOM,Node也提供了一些新的模塊例如http,fs模塊等。Node.js 使用了事件驅動非阻塞式 I/O 的模型,使其輕量又高效而且Node.js 的包管理器 npm,是全球最大的開源庫生態系統。事件驅動與非阻塞IO後面咱們會一一介紹。到此咱們已經對node有了簡單的概念。java

2.Node解決了哪些問題?

Node在處理高併發,I/O密集場景有明顯的性能優點node

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

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

3.進程與線程

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

3.1 談談瀏覽器

瀏覽器模型

  • 用戶界面-包括地址欄、前進/後退按鈕、書籤菜單等
  • 瀏覽器引擎-在用戶界面和呈現引擎之間傳送指令(瀏覽器的主進程)
  • 渲染引擎,也被稱爲瀏覽器內核(瀏覽器渲染進程)
  • 一個插件對應一個進程(第三方插件進程)
  • GPU提升網頁瀏覽的體驗(GPU進程)

因而可知瀏覽器是多進程的,而且從咱們的角度來看咱們更加關心瀏覽器渲染引擎npm

3.2 渲染引擎

渲染引擎內部是多線程的,內部包含兩個最爲重要的線程ui線程和js線程。這裏要特別注意ui線程和js線程是互斥的,由於JS運行結果會影響到ui線程的結果。ui更新會被保存在隊列中等到js線程空閒時當即被執行。後端

3.3 其餘線程

  • 瀏覽器事件觸發線程(用來控制事件循環,存放setTimeout、瀏覽器事件、ajax的回調函數)
  • 定時觸發器線程(setTimeout定時器所在線程)
  • 異步HTTP請求線程(ajax請求線程)

3.4 js單線程

javascript在最初設計時設計成了單線程,爲何不是多線程呢?若是多個線程同時操做DOM那豈不會很混亂?這裏所謂的單線程指的是主線程是單線程的,因此在Node中主線程依舊是單線程的。咱們來張圖看看單線程和多線程promise

  • tomcat,iis服務
    多線程
  • node服務
    單線程

單線程特色是節約了內存,而且不須要在切換執行上下文。並且單線程不須要管鎖的問題,這裏簡單說下鎖的概念。例以下課了你們都要去上廁所,廁所就一個,至關於全部人都要訪問同一個資源。那麼先進去的就要上鎖。而對於node來講。下課了就一我的去廁所,因此免除了鎖的問題!瀏覽器

4.隊列和棧

隊列和棧

  • 隊列
setTimeout(function(){
    console.log(1)
})
setTimeout(function(){
    console.log(2)
})
setTimeout(function(){
    console.log(3)
})
複製代碼

當設置定時器時,會將定時器對應的回調函數依次的放到隊列中,執行時按照放置的順序依次執行。tomcat

function stack(){
    console.log(1);
    fn1();
    function fn1(){
        console.log(2);
        fn2();
        function fn2(){
            console.log(3)
        }
    }
}
stack();
複製代碼

咱們能夠看到js是在全局的上下文中執行的,調用棧入棧的順序是stack->fn1->fn2,看成用域銷燬時先須要先銷燬fn2->fn1->stack

5.瀏覽器中的Event Loop

eventLoop

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

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

6.Node中的Event Loop

咱們先來張圖看看node是如何工做的

node system

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

在libuv內部有這樣一個事件環機制。在node啓動時會初始化事件環

┌───────────────────────┐
┌─>│     timers(計時器)     │
|  |   執行setTimeout以及   |
|  |   setInterval的回調。  |
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     |
│  | 處理網絡、流、tcp的錯誤 |
|  | callback              |
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
|  |     node內部使用       |
│  └──────────┬────────────┘      
│  ┌──────────┴────────────┐       ┌───────────────┐ 
│  │       poll(輪詢)      │       │   incoming:   │
|  | 執行poll中的i/o隊列    | <─────┤  connections, │
|  | 檢查定時器是否到時      |       │   data, etc.  |     
│  └──────────┬────────────┘       └───────────────┘    
│  ┌──────────┴────────────┐      
│  │      check(檢查)      │
|  | 存放setImmediate回調   |
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    |
   │ 關閉的回調例如         |
   | sockect.on('close')   |
   └───────────────────────┘
複製代碼

這裏每個階段都對應一個事件隊列,當event loop執行到某個階段時會將當前階段對應的隊列依次執行。當隊列執行完畢或者執行的數量超過上線時,會轉入下一個階段。這裏咱們重點關注poll階段

6.1 poll階段

poll 流程圖

6.2 setTimeout 和 setImmediate

兩者很是類似,可是兩者區別取決於他們何時被調用.

  • setImmediate 設計在poll階段完成時執行,即check階段;
  • setTimeout 設計在poll階段爲空閒時,且設定時間到達後執行;但其在timer階段執行 其兩者的調用順序取決於當前event loop的上下文,若是他們在異步i/o callback以外調用(在i/o內調用由於下一階段爲check階段),其執行前後順序是不肯定的,須要看loop的執行前的耗時狀況。
setTimeout(function timeout () {
  console.log('timeout');
},0);

setImmediate(function immediate () {
  console.log('immediate');
});
複製代碼

6.3 process.nextTick

nextTick並不屬於事件循環的某個階段,他的執行方式是在各個階段切換的中間執行,來段噁心的代碼

setImmediate(function(){
    console.log(1);
    process.nextTick(function(){
        console.log(2);
    });
});
process.nextTick(function(){
    console.log(3);
    setImmediate(function(){
        console.log(4);
    })
});
// 3 1 4 2
複製代碼

這裏就不解釋了,若是你懂了,說明就明白nextTick的執行時機了!(nextTick不要遞歸調用,不然後面階段的callback將沒法執行)

7.宏任務和微任務

任務可分爲宏任務和微任務

  • macro-task(宏任務): setTimeout, setInterval, setImmediate, I/O
  • micro-task(微任務): process.nextTick, 原生Promise(有些實現的promise將then方法放到了宏任務中),Object.observe, MutationObserver
process.nextTick > promise.then > setTimeout > setImmediate
複製代碼

經過上面的學習咱們知道了任務的執行順序,要注意的是Promise.then方法被定義在了nextTick以後執行

8 同步異步和阻塞非阻塞

同步異步取決於被調用者,阻塞非阻塞取決於調用者

  • 阻塞調用是指調用結果返回以前,當前線程會被掛起。調用線程只有在獲得結果以後纔會返回。
  • 非阻塞調用指在不能馬上獲得結果以前,該調用不會阻塞當前線程。

model

到此咱們詳細的解說了一下node中的概念,喜歡的點個贊吧^_^! 支持個人能夠給我打賞

相關文章
相關標籤/搜索