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 提供了 exports 和 require 兩個對象,其中 exports 是模塊公開的接口,require 用於從外部獲取一個模塊的接口,即所獲取模塊的 exports 對象。
(3)創建包
包是將某個獨立的功能封裝起來,用於發佈、更新、依賴管理和版本控制。Node.js使用npm來解決包的發佈和獲取需求。