Node.js 指南(阻塞與非阻塞概述)

阻塞與非阻塞概述

此概述介紹了Node.js中阻塞與非阻塞調用之間的區別,此概述將引用事件循環和libuv,但不須要事先了解這些主題,假設讀者對JavaScript語言和Node.js回調模式有基本的瞭解。數據庫

「I/O」主要指與 libuv支持的系統的磁盤和網絡的交互。

阻塞

阻塞是指在Node.js進程中執行其餘JavaScript必須等到非JavaScript操做完成,發生這種狀況是由於在發生阻塞操做時,事件循環沒法繼續運行JavaScript。segmentfault

在Node.js中,因爲CPU密集而不是等待非JavaScript操做而表現出較差性能的JavaScript,例如I/O,一般不稱爲阻塞。Node.js標準庫中使用libuv的同步方法是最經常使用的阻塞操做,原生模塊也可能具備阻塞方法。安全

Node.js標準庫中的全部I/O方法都提供非阻塞的異步版本,並接受回調函數,某些方法還具備對應的阻塞方法,其名稱以Sync結尾。服務器

比較代碼

阻塞方法同步執行,非阻塞方法異步執行。網絡

以文件系統模塊爲例,這是一個同步讀取文件的方法:併發

const fs = require('fs');
const data = fs.readFileSync('/file.md'); // blocks here until file is read

這是一個等效的異步示例:異步

const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
  if (err) throw err;
});

第一個示例看起來比第二個示例更簡單,但缺點是第二行阻止執行任何其餘JavaScript,直到讀取整個文件,請注意,在同步版本中,若是拋出錯誤,則須要捕獲它,不然進程將崩潰,在異步版本中,由做者決定是否應該如圖所示拋出錯誤。函數

讓咱們稍微擴展一下咱們的例子:性能

const fs = require('fs');
const data = fs.readFileSync('/file.md'); // blocks here until file is read
console.log(data);
// moreWork(); will run after console.log

這是一個相似但不等同的異步示例:ui

const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
  if (err) throw err;
  console.log(data);
});
// moreWork(); will run before console.log

在上面的第一個示例中,將在moreWork()以前調用console.log,在第二個示例中,fs.readFile()是非阻塞的,所以JavaScript執行能夠繼續,而且將首先調用moreWork(),在不等待文件讀取完成的狀況下運行moreWork()的能力是一個關鍵的設計選擇,能夠提升吞吐量。

併發和吞吐量

Node.js中的JavaScript執行是單線程的,所以併發性是指事件循環在完成其餘工做後執行JavaScript回調函數的能力,任何預期以併發方式運行的代碼都必須容許事件循環繼續運行,由於非JavaScript操做(如I/O)正在發生。

做爲一個例子,讓咱們考慮這樣一種狀況:每一個Web服務器請求須要50ms才能完成,50ms中的45ms是能夠異步完成的數據庫I/O,選擇非阻塞異步操做能夠釋放每一個請求45毫秒來處理其餘請求,僅經過選擇使用非阻塞方法而不是阻塞方法,這是容量的顯着差別。

事件循環不一樣於許多其餘語言中的模型,其中能夠建立其餘線程來處理併發工做。

混合阻塞和非阻塞代碼的危險

處理I/O時應該避免一些模式,咱們來看一個例子:

const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
  if (err) throw err;
  console.log(data);
});
fs.unlinkSync('/file.md');

在上面的例子中,fs.unlinkSync()極可能在fs.readFile()以前運行,這會在實際讀取以前刪除file.md,寫一個更好的方法是徹底無阻塞並保證以正確的順序執行:

const fs = require('fs');
fs.readFile('/file.md', (readFileErr, data) => {
  if (readFileErr) throw readFileErr;
  console.log(data);
  fs.unlink('/file.md', (unlinkErr) => {
    if (unlinkErr) throw unlinkErr;
  });
});

上面在fs.readFile()的回調中對fs.unlink()進行了非阻塞調用,這保證了正確的操做順序。

其餘資源


上一篇:遷移到安全的Buffer構造函數

下一篇:Node.js事件循環、定時器和process.nextTick()

相關文章
相關標籤/搜索