JavaScript中讀取文件的各種方法
在JavaScript中,尤其是配合Node.js這樣的運行時環境,有多種方式可以讀取服務器上的文件。每種方法都有其適用場景和優缺點。今天,我們就來比較幾種常見的文件讀取方法,看看哪一種最適合你的需求。
太長不看版
如果你在尋找一個快速且簡單的方法,fs.promises是一個不錯的選擇,它提供了一個Promise接口,讓你能夠以現代的異步語法來處理文件讀取。然而,如果你更傾向于同步方法,你可能會更喜歡fs.readFileSync,雖然它會阻塞當前線程直到文件讀取完成。最后,如果你在尋找一種能夠提供更好的性能的方法,你可能會想要考慮使用fs.readFile,這是Node.js中最傳統的異步文件讀取方法,使用回調函數來處理結果。
使用fs.promises
const fs = require('fs/promises');
const readFile = fs.readFile;
readFile("lipsum.txt", { encoding: 'utf-8' })
.then((data) => {...})
.catch((err) => {...});
這種方法使用了Node.js提供的fs.promises接口,它返回一個Promise對象,可以鏈式調用.then()和.catch()方法來處理異步操作的結果。
使用fs.readFile和util.promisify
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);
readFile("lipsum.txt", { encoding: 'utf-8' })
.then((data) => {...})
.catch((err) => {...});
這種方法通過util.promisify將傳統的回調函數轉換成返回Promise的函數,使得你可以用更現代的異步語法來處理文件讀取。
使用fs.readFileSync
const fs = require('fs');
const readFileSync = fs.readFileSync;
var data = readFileSync("lipsum.txt", { encoding: 'utf-8' });
這是同步方法,它會阻塞當前線程直到文件讀取完成,適用于對性能要求不高且文件不大的情況。
使用await和fs.readFileSync
const fs = require('fs');
const readFileSync = fs.readFileSync;
async function f(name, options) {
return readFileSync(name, options);
}
這種方法結合了async/await語法和同步讀取,可以在需要同步行為但希望代碼看起來更異步的情況下使用。
使用fs.readFile
const fs = require('fs');
const readFile = fs.readFile;
readFile('lipsum.txt', function read(err, data) {...});
這是Node.js中最傳統的異步文件讀取方法,使用回調函數來處理結果。
性能比較
我進行了一項小的基準測試,重復讀取磁盤上的同一個文件,并記錄讀取文件50,000次所需的毫秒數。文件相對較小,略多于一千字節。我使用的是一臺擁有數十個Ice Lake Intel核心和大量內存的大型服務器。測試使用了Node.js 20.1和Bun 1.0.14。Bun是一個與Node.js競爭的JavaScript運行時[1]。
多次運行基準測試,我報告了所有情況下的最佳結果。你的結果可能會有所不同。
方法 | Node.js 時間 | Bun 時間 |
fs.promises | 2400 毫秒 | 110 毫秒 |
fs.readFile 和 util.promisify | 1500 毫秒 | 180 毫秒 |
fs.readFileSync | 140 毫秒 | 140 毫秒 |
await fs.readFileSync | 220 毫秒 | 180 毫秒 |
fs.readFile | 760 毫秒 | 90 毫秒 |
至少在我的系統上,在這次測試中,使用Node.js時fs.promises明顯比其他方法成本更高。Bun運行時在這次測試中比Node.js快得多。
結果看起來對fs.promises更不利,因為readFileSync使用了300毫秒的CPU時間,而fs.promises使用了7秒的CPU時間。這是因為fs.promises在基準測試期間觸發了多個核心的工作。
將文件大小增加到32kB并不會改變結論。如果你使用更大的文件,許多Node.js的案例會因“堆限制Allocation failed”而失敗。Bun即使在大文件下也能繼續工作。測試結果在Bun中沒有改變結論:在我的測試中,即使對于大文件,fs.readFile也是一致更快的。
本文譯自:https://lemire.me/blog/2024/03/12/how-to-read-files-quickly-in-javascript/
Reference
[1]Bun是一個與Node.js競爭的JavaScript運行時: https://bun.dev/