為什麼nodejs的module.js里用了readFileSync而不用readFile?
// Native extension for .js
Module._extensions[".js"] = function(module, filename) {
var content = fs.readFileSync(filename, "utf8");
module._compile(internalModule.stripBOM(content), filename);
};// Native extension for .json
為什麼會選擇使用同步而不用非同步實現呢。
Module._extensions[".json"] = function(module, filename) {
var content = fs.readFileSync(filename, "utf8");
try {
module.exports = JSON.parse(internalModule.stripBOM(content));
} catch (err) {
err.message = filename + ": " + err.message;
throw err;
}
};
如 @魯小夫 所說,這是因為 require() 是同步的。
進一步說,之所以同步是 Node.js 所遵循的 CommonJS 的模塊規範要求的。在當年,CommonJS 社區對此就有很多爭議,導致了堅持非同步的 AMD 從 CommonJS 中分裂出來。
CommonJS 模塊是同步載入和同步執行,AMD 模塊是非同步載入和非同步執行,CMD(Sea.js)模塊是非同步載入和同步執行。ES6 的模塊體系最後選擇的是非同步載入和同步執行。也就是 Sea.js 的行為是最接近 ES6 模塊的。不過 Sea.js 這樣做是需要付出代價的——需要掃描代碼提取依賴,所以它不像 CommonJS/AMD 是純運行時的模塊系統。
注意 Sea.js 是 2010年之後開發的,提出 CMD 更晚。Node.js 當年(2009年)只有 CommonJS 和 AMD 兩個選擇。就算當時已經有 CMD 的等價提案,從性能角度出發,Node.js 不太可能選擇需要靜態分析開銷的 類 CMD 方案。考慮到 Node.js 的模塊是來自於本地文件系統,最後 Node.js 選擇了看上去更簡單的 CommonJS 模塊規範,直到今天。我不懂NodeJS,所以說錯了我就再補充好了
為什麼用readFileSync,因為require函數被要求是同步的,所以後面主要說為什麼require是同步的
誠然我們可以說因為CommonJS是同步的,而NodeJS遵循CommonJS規範所以它是同步的
但這顯然沒有任何意義,為什麼NodeJS要選擇CommonJS,以Issac這麼固執的思路和NodeJS在正式發布前就存在的影響力而言,這貨自己造一套模塊規範都不成問題,你看在非同步的方案選型上NodeJS不就玩出了清新脫俗的單個callback模式么,更別說堅持node_modules目錄不能變更一輩子的信仰(不能再說了,不然黑不完了……)所以到底為什麼require就得是要同步的呢,我們可以從不同的角度來看這事情
從模塊規範的角度來看,依賴的同步獲取是幾乎所有模塊機制的首選,是符合由無數的語言奠定的開發者的直覺的。即便以非同步為名的AMD,其模塊內部的依賴在大部分場景下也是同步的,只有按需載入或者總入口等幾個特殊場景才會使用非同步的require
因此,NodeJS的require是同步的,這是自然而然的選擇但是NodeJS本身是非同步為基礎的啊,在經過NodeJS社區和各位傳道者孜孜不倦的教誨後,我們已經接受了一個公理:IO操作不非同步是很糟糕的。所以會有這個問題,你看NodeJS自己在糟糕著是的,同步的IO操作會導致在NodeJS上出現一些問題,但這些問題是會在module這個模塊里遇到並且成為瓶頸的嗎,我不認為
我們看一下模塊有個什麼樣的特點:- 一個系統的模塊是有限的,除非有人神經了創造出一個用模塊代替數據的玩法
- 模塊是有緩存的,即多次的require不會產生多次的IO操作
以上面2點為前提,我們也可以有一個結論:
在一個系統的運行周期內,require產生的IO操作次數是有限的
這不同於其它的IO(網路、文件、子進程),是會隨著系統的運行和輸入不斷積累增加的
而有限的IO操作是很難成為系統的瓶頸的,如果你說無論怎麼樣在系統運行的過程任何Block IO都不應該出現,那我們可以找遍所有的require在合適的時機進行處理,比如系統啟動時的預熱你說預熱太麻煩?事實上從模塊到Class Loader,到資料庫鏈接,到內存緩存,到分級硬碟緩存,到虛擬機預跑,預熱就是大型系統的一部分,不爽不要玩以上,是從模塊本身的特性來說的,結論就是使用非同步的require收益很小,同時對開發者並不友好
但我們總不能避免會有很極端的開發者存在,人家牛到可以接受非同步的依賴管理,可以追求極致的Non Blocking IO,並且人家想要任何可能存在的收益這時候我們就要算ROI了,對NodeJS的實現者來說,其ROI夠嗎可能很多人並不知道,NodeJS最開始是有非同步的require的,叫做require.async,直到0.3.0還是0.0.3版本才被移除
在NodeJS社區當時也是有過對移除require.async的討論Issue的,只是我現在找不到了,自從Node和IO合併以後很多以前的資料都不好找,無論是在archive repo還是在當前的repo都不好找……require.async的實現其實是很麻煩的,我們就舉一個最稀鬆平常的案例:一個require.async引起的readFile還沒結束時,又使用require.async獲取同一個模塊怎麼辦
產生2次readFile操作嗎?這顯然有了額外的開銷,同時一個模塊被執行2次是不可以的,有些模塊的執行是有副作用的
復用第一次的readFile嗎?這就需要一個callback的管理機制,這就是降低ROI的地方了這只是一個普通的案例,你不如試想如果是一個require.async進行的過程中出現一個同步的require怎麼辦,這回可真是玩脫了……而類似的邊界情況還有很多很多,這導致require.async這個函數相當的不穩定,很容易在各種順序的調用中出現奇怪的狀況,那麼直接移掉這鬼東西也是可以理解的綜上,所以我認為Node的require同步是非常合理的選擇,因此推導出module.js里用readFileSync也是非常合理的選擇因為 require() 載入模塊是同步操作
nodejs 讀本地文件速度快啊。用不用非阻塞io都不太影響啊。像requirejs這樣的網路時延慢的就用非同步啦,不會阻塞住可以並行搞。
不會阻塞住可以並行搞
用不用非阻塞io都不太影響啊。
require一般是在腳本第一次執行時執行的,這時應用程序應該在啟動階段,對性能要求不高,所以同步非同步無所謂啦~哪種方法容易用就用哪種。
這是 node.js 的一個設計缺陷
推薦閱讀:
※想學習nodejs 有什麼書可以推薦的?
※《深入淺出Node.js》《Node.js 實戰(雙色)》《了不起的Node.js》 這三本書那本書比較好呢?
※怎麼才能成為一個nodejs大神?
※ECMAScript 6 的模塊相比 CommonJS 的require (...)有什麼優點?
※a=new b()和a=b(),其本質的區別在哪?
TAG:JavaScript | Nodejs |