nodejs中的阻塞和非阻塞(https://nodejs.org/en/docs/guides/blocking-vs-non-blocking/)

一、簡述

這片文章主要講述Nodejs中的阻塞和非阻塞回調,涉及到事件循環和libuv。但是你並不需要提前瞭解這些知識,你只需要具備js和Nodejs回調模式的基礎。

"I/O"指的是與系統硬盤的交互以及libuv所支持的網絡交互。

1.Blocking阻塞

1.1代碼對比

阻塞指的是Nodejs中的js進程必須等待非js操作完成之後才能執行,原因是當阻塞操作發生時,事件循環無法持續運行。

在nodejs中,由於cpu內存不夠而造成js運行性能差的情況不叫阻塞,只有js在等待非js操作(例如:I/O)完成時才運行的情況叫做阻塞。Nodejs標準庫中的同步方法是常用的阻塞操作。原生模塊中也有阻塞方法。

Nodejs標準庫中的所有IO方法都提供了異步非阻塞的版本,這些方法接收回調函數。一些方法有其對應的阻塞版本,以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;
});

阻塞方法相對非阻塞方法來說比較簡單,但是,有其缺點:它會等到文件讀取完成後才執行後續操作。需要注意的是,在阻塞方法中,錯誤必須被捕獲,否則程序就會崩潰。但是在非阻塞版本中,可以不進行錯誤的捕獲。

2.併發性和吞吐量(Concurrency and Throughput)

Nodejs中執行js是單線程的,所以,併發性指的是執行事件回調函數的能力。併發性強的代碼即使在非js操作發生的時候,也能繼續執行事件回調。
舉例:每一個到達服務器的請求會花費50ms完成,其中的45ms用來對數據庫的異步讀取。選擇非阻塞的異步方法可以釋放服務器45ms的時間去處理其他請求。這是選擇非阻塞方法的最明顯的優勢。
js中的事件循環模型與其他語言不同的地方在於:其他語言可以使用多線程來處理併發性工作。


3.混合阻塞方法和非阻塞方法的危險之處

當處理IO時有一些需要避免的模式:

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方法之前運行,這樣就會導致,文件在沒有讀取完成前被刪除。最好的方法如下:

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





發佈了66 篇原創文章 · 獲贊 6 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章