Node.js: コールバックの処理
再帰でサブディレクトリを探索
下はサブディレクトリのリストを得るサンプルです。 再帰呼び出し を使用して 2階層以下のディレクトリも取得します。
先にコールバックを使わない単純な例
/* 再帰でサブディレクトリを探索 node TestNodejs_fs_subdir1.js node TestNodejs_fs_subdir1.js (開始ディレクトリ) 実行例: node TestNodejs_fs_subdir1.js .. 1つ上のディレクトリから探索 */ const FS = require("fs"); const PATH = require("path"); let startdir = process.argv[2]; //コマンドパラメータで指定 if (!startdir){ startdir = "."; //パラメータがない場合は現在のディレクトリから } console.log("##startdir:"+startdir); const list = getSubDirSync(startdir, []); for (const path of list){ console.log(path); } /* 再帰でサブディレクトリを探索 @param {string} dir 探索するディレクトリ @param {Array} dirlist ディレクトリリストを格納する配列 @return {Array<string>} 結果のディレクトリリスト */ function getSubDirSync(dir, dirlist){ const list = FS.readdirSync(dir); for (const filename of list){ const path = PATH.join(dir, filename); try{ const st = FS.statSync(path); if (st.isDirectory()){ dirlist.push(path); getSubDirSync(path, dirlist); //再帰呼び出し } } catch(e){ console.log(e.message); //throw e; } } return dirlist; }
上のサンプルの問題点
上のサンプルにはいくつかの問題点があります。
汎用の処理ではない
上のサンプルではサブディレクトリのリスト取得の専用処理になり、
他の用途に使えません。たとえば、
1) 指定ディレクトリ以下のファイル名だけ取得したい。
2) 1) に加えて特定の拡張子や特定の日付のファイルだけ取得したい。
という場合は、
処理内容が少し違うだけのコードを毎回書く必要があり面倒です。
逐次処理ができない
処理によっては次のような話も出てきます。
- 途中で処理を中止したい。
例えば GUI で中止ボタンが付いているときの処理があります。 上のサンプルでは途中で処理を中止する仕組みが入っていないので 強制終了させる以外に止める手段がありません。 - 取得できた項目からすぐに次の処理を行いたい。
上のサンプルではすべてのリストを取得した後で次の処理を行っています。 このようにせず 取得できた項目からすぐに次の処理を行いたい場合もあります。 無駄な待ち時間やメモリ消費を少なくしたいときに このような処理を行うことがあります。
コールバックで再帰探索処理を汎用化
上のサンプルをコールバックを使って汎用化します。
JavaScript コールバック処理
/* 再帰でサブディレクトリを探索 node TestNodejs_fs_subdir2.js node TestNodejs_fs_subdir2.js (開始ディレクトリ) 実行例: node TestNodejs_fs_subdir2.js C:\ C:\ から探索 */ const FS = require("fs"); const PATH = require("path"); let startdir = process.argv[2]; //コマンドパラメータで指定 if (!startdir){ startdir = "."; //パラメータがない場合は現在のディレクトリから } recurseDir(startdir, //ファイルやディレクトリが見つかったときの処理 function(path, stat){ if (stat.isDirectory()){ //ディレクトリの場合 console.log(path); } }, //例外が発生したときの処理 function(path, e){ console.log("エラー:" + path); throw e; //例外を投げると処理終了。この行をコメントにすると処理を継続する。 }); /* サブディレクトリを再帰探索 @callback find(path, stat) ファイルやディレクトリが見つかったときの処理 {string} path : パス {Object} stat : fs.Stats のオブジェクト @callback error(path, e) 省略可。例外が発生したときの処理 {string} path : パス {Object} e : 例外オブジェクト */ function recurseDir(dir, find, error){ const list = FS.readdirSync(dir); for (const filename of list){ const path = PATH.join(dir, filename); try{ const st = FS.statSync(path); find(path, st); if (st.isDirectory()){ recurseDir(path, find); //再帰探索 } } catch(e){ if (error){ error(path, e); } } } }
function recurseDir() は汎用の再帰探索処理で、 呼び出し側のコールバックの内容を変えることにより ディレクトリの代わりにファイルリストの取得処理にしたり、 さらに拡張子や日付など細かい条件をつけた抽出処理にしたりすることができます。
汎用の処理は別のファイルに分割してライブラリ化し、
他のプログラムからも使えるようにします。
Node.js ライブラリに分割 に続く。