1.阻塞與線程

什麼是阻塞(block)呢?

線程在執行中如果遇到磁盤讀寫或網絡通信(統稱為 I/O 操作),通常要耗費較長的時間,這時操作系統會剝奪這個線程的 CPU 控制權,使其暫停執行,同時將資源讓給其他的工作線程,這種線程調度方式稱為 阻塞。當 I/O 操作完畢時,操作系統將這個線程的阻塞狀態解除,恢復其對CPU的控制權,令其繼續執行。

這種 I/O 模式就是通常的同步式 I/O(Synchronous I/O)或阻塞式 I/O(Blocking I/O)。

 

異步式 I/O (Asynchronous I/O)或非阻塞式 I/O (Non-blocking I/O)則針對所有 I/O 操作不採用阻塞的策略。

當線程遇到 I/O 操作時,不會以阻塞的方式等待 I/O 操作的完成或數據的返回,而只是將 I/O 請求發送給操作系統,繼續執行下一條語句。當操作系統完成 I/O 操作時,以事件的形式通知執行 I/O 操作的線程,線程會在特定時候處理這個事件。

為了處理異步 I/O,線程必須有事件循環,不斷地檢查有沒有未處理的事件,依次予以處理。

 

Node.js使用的是單線程、非阻塞的時間編程模式。

2.回調函數

Node.js使用異步的方式讀取文件:

//readfile.js 
var fs = require('fs'); 
fs.readFile('file.txt', 'utf-8', function(err, data) { 
 if (err) { 
   console.error(err); 
 } else { 
   console.log(data); 
 } 
}); 
console.log('end.');

運行的結果如下:

  end.

  Contents of the file. 

 

3.事件

Node.js 所有的異步 I/O 操作在完成時都會發送一個事件到事件隊列。

事件由EventEmitter對象提供,

//event.js

var EventEmitter = require('events').EventEmitter; 
var event = new EventEmitter(); 
event.on('some_event', function() { 
   console.log('some_event occured.'); 
}); 
setTimeout(function() { 
   event.emit('some_event'); 
}, 1000);

1秒後控制枱輸出了 some_event occured.。其原理是 event 對象註冊了事件 some_event 的一個監聽器,

然後我們通過 setTimeout 在1000毫秒以後向event 對象發送事件 some_event,此時會調用 some_event 的監聽器

Node.js的事件循環機制

Node.js 程序由事件循環開始,到事件循環結束,所有的邏輯都是事件的回調函數,所以 Node.js 始終在事件循環中。

事件的回調函數在執行的過程中,可能會發出 I/O 請求或直接發射(emit)事件,執行完畢後再返回事件循環,事件循環會檢查事件隊列中有沒有未處理的事件,直到程序結束。

 

4.模塊和包

Node.js提供了require函數來調用其他模塊,而且模塊都是基於文件的,機制十分簡單。

(1)什麼是模塊?

模塊是 Node.js 應用程序的基本組成部分,文件和模塊是一一對應的。換言之,一個Node.js 文件就是一個模塊,這個文件可能是 JavaScript 代碼、JSON 或者編譯過的 C/C++ 擴展。

 

(2)創建以及加載模塊

Node.js 提供了 exportsrequire 兩個對象,其中 exports 是模塊公開的接口,require 用於從外部獲取一個模塊的接口,即所獲取模塊的 exports 對象。

 

(3)創建包

包是將某個獨立的功能封裝起來,用於發佈、更新、依賴管理和版本控制。Node.js使用npm來解決包的發佈和獲取需求。