Skip to content

Latest commit

 

History

History
148 lines (121 loc) · 4.13 KB

2-readfile-async.md

File metadata and controls

148 lines (121 loc) · 4.13 KB

7.2 非同步式讀取多個檔案

以上是讀取一個檔案的範例。現在,讓我們來改寫程式:

  • 讀取 index.txt 檔案,index.txt 列出多個文字檔
  • 再讀取 index.txt 裡列出的檔案

例如,index.txt 的內容如下:

chapter1.txt
chapter2.txt
chapter3.txt
chapter4.txt
chapter5.txt

再分別去讀取上述共 5 個檔案的內容。撰寫這個練習題,要先分析幾個觀念:

  • 以 Line-by-Line 方式讀取 index.txt
  • 讀取 index.txt 的動作,是 Non-blocking IO
  • 等到 index.txt 讀取完成後,再開始讀取其它檔案
  • 如上,在 Callback Function 裡,撰寫「讀取其它檔案」的程式碼

有一個 Node.js 的模組:Node-BufferedReader,可以協助我們做 Line-by-Line 的讀取。必須先安裝這個模組:

$ npm i buffered-reader

用法如下:

 1 var reader = require ("buffered-reader");
 2 var DataReader = reader.DataReader;
 3 
 4 new DataReader ('index.txt', { encoding: "utf8" })
 5     .on ("error", function (error){
 6         console.log(error);
 7     })
 8     .on ("line", function (line, nextByteOffset){
 9         console.log("[LINE] " + line);
10     })
11     .on ("end", function (){
12         // finish reading
13     })
14     .read();

buffered-reader 會逐行讀取 'index.txt',完成單行讀取後,觸發 line 事件,並 Callback 一個函數。這部份實作在第 8 行,Callback Function 的第一個參數 line 存放讀取到的內容。

接著,將檔名儲存到陣列裡備用:

 1 // the file list
 2 var files = [];
 3 
 4 new DataReader (path + 'index.txt', { encoding: "utf8" })
 5     .on ("error", function (error){
 6         console.log (error);
 7     })
 8     .on ("line", function (line, nextByteOffset){
 9         files.push({
10             filename: line
11         });
12     })
13     .on ("end", function (){
14         // finish reading
15     })
16     .read();

要如何讀取 files 陣列裡的檔案呢?只要撰寫一個迴圈即可:

1 for (var i = 0; i < files.length; i++) {
2 	var filename = files[i].filename;
3 
4     fs.readFile(filename, 'utf8', function(err, data) {
5 	    console.log("[DATA] " + data);
6 	});
7 }

這段程式碼,是本章的另一個重點:

  • 第 4 行是 Non-blocking IO
  • 所以不會等到 readFile() 讀完檔案,會立即往下執行,進入下一個迴圈

這個迴圈並不會因為「讀取檔案」,而花費太多的執行時間,這就是 Node.js 能做到「efficient」與「real-time」的關鍵。完整的程式碼如下:

 1 // public modules
 2 var util = require('util');
 3 var fs = require('fs');
 4 var reader = require ("buffered-reader");
 5 var DataReader = reader.DataReader;
 6 
 7 // the file list
 8 var files = [];
 9 
10 /**
11  * Use BufferedReader to read text file line by line.
12  *
13  * See: https://github.com/Gagle/Node-BufferedReader
14  */
15 var getFilelist = function(path, cb) {
16     new DataReader (path + 'index.txt', { encoding: "utf8" })
17         .on ("error", function (error){
18             console.log (error);
19         })
20         .on ("line", function (line, nextByteOffset){
21             files.push({
22                 filename: line
23             });
24         })
25         .on ("end", function (){
26             cb(files);
27         })
28         .read();
29 };
30 
31 // Application
32 getFilelist('manuscript/', function(files) {
33     for (var i = 0; i < files.length; i++) {
34     	var filename = 'manuscript/' + files[i].filename;
35 
36         fs.readFile(filename, 'utf8', function(err, data) {
37     	    console.log("[DATA] " + data);
38     	});
39     }
40 });

以下是執行結果:

[DATA] This is chapter 1.

[DATA] This is chapter 2.

[DATA] This is chapter 3.

[DATA] This is chapter 4.

[DATA] This is chapter 5.

要特別注意的是,上述的訊息順序是「非確定性」。由於 readFile 是 Non-blocking IO,所以「先讀取的檔案」,不一定「先完成讀取」。例如,'chapter1.txt' 的檔案很大時,就可能比其它後續的檔案,更晚完成讀取。