Node.js API (process) - コマンドライン引数の取得
Node.js を使うと JavaScript を PC 上で動作できるようになります。
プログラムとして動作させる場合、コマンドライン引数を使いたくなることがあります。
今回は Node.js でコマンドライン引数を取得する方法の紹介です。
コマンドライン引数
コマンドライン引数は process.argv の配列に格納されています。process は 標準モジュールの 一つである Process モジュールが提供する グローバルオブジェクトで、プログラムのどこからでも使えます。
process_arg.js :
for (let cnt in process.argv) { console.log('%d : %s', cnt, process.argv[cnt]); }
$ node --no-deprecation process_arg.js foo bar 0 : c:\Program Files\nodejs\node.exe 1 : d:\home\programmers_notes\node\process_arg\process_arg.js 2 : foo 3 : barprocess.argv では 先頭に node のパス、 2 番めにスクリプトファイルのパスが格納され、 引数はそれ以降です。 process.argv は node プログラム自体に渡される引数は入っていません。
あまり必要になることはありませんが、 もしそれらの引数が必要な場合は process.execArgv に格納されています。 また、 node やスクリプトファイルのパスは process.argv 以外から取得することもできます。
メソッド(オブジェクト) | 対象 |
---|---|
process.argv | スクリプトの引数の配列 |
process.execArgv | node の引数の配列 |
process.execPath | node のパス |
__dirname | スクリプトファイルのディレクトリーのパス |
__filename | スクリプトファイルのパス |
console.log('process.execArgv = ', process.execArgv); console.log('process.execPath = ', process.execPath); console.log('__dirname = ', __dirname); console.log('__filename = ', __filename);
process.execArgv = [ '--no-deprecation' ] process.execPath = c:\Program Files\nodejs\node.exe __dirname = d:\home\programmers_notes\node\process_arg __filename = d:\home\programmers_notes\node\process_arg\process_arg.js
npm からの起動
npm の scripts のstart
などにコマンドを書くことによって、
npm から起動することができます。
package.json :
{ "name": "process_arg", "version": "1.0.0", "main": "process_arg.js", "scripts": { "start": "node process_arg.js" }, "license": "ISC" }npm から起動した場合、引数は npm の引数となります。 スクリプトに引数を渡したい場合は "--" 以降に記述します。
$ npm start -- foo bar > process_arg@1.0.0 start d:\home\programmers_notes\node\process_arg > node process_arg.js "foo" "bar" 0 : C:\Program Files\nodejs\node.exe 1 : d:\home\programmers_notes\node\process_arg\process_arg.js 2 : foo 3 : bar process.execArgv = [] process.execPath = C:\Program Files\nodejs\node.exe __dirname = d:\home\programmers_notes\node\process_arg __filename = d:\home\programmers_notes\node\process_arg\process_arg.js
Node.js API - JavaScript のタイマー機能と非同期呼び出し
今回は Node.js API を使ったタイマー機能とそれに関連する関数の非同期呼び出しについて説明します。
Node.js API にある機能ですが、もともと JavaScript にもある機能なので、ブラウザーが対応していればクライアントサイドでも利用できます。
クライアントサイドで使う場合は window. をつけたものに読み替えてください。(例 : setTimeout → window.setTimeout)
タイマーの設定
setTimeout
タイマー機能で最も基本的な関数は setTimeout() です。 これは指定した時間がたった後に関数を実行します。setTimeout(func, delay [, arg0, arg1, ...])delay が指定する時間で ミリ秒 で設定します。 以下の例では 5 秒後にログを出力します。
setTimeout(() => { console.log('Timeout'); }, 5000);実行する関数が引数をとる場合には delay の後に引数を渡します。
setTimeout((a, b) => { console.log('Timeout', a, b); // Timeout foo bar }, 5000, 'foo', 'bar');ただ、無名関数を使う場合はクロージャーの機能で変数を持たせることができるので、使う必要はないです。
setInterval
setTimeout() では渡した関数を一回実行して終わりですが、 setInterval() の場合は指定した間隔で繰り返し呼び出します。 それ以外は setTimeout() と同じです。setInterval(func, delay [, arg0, arg1, ...])以下の例では 1 秒間隔でログを出力します。 ただ、これを本当に実行すると止まらなくなります。
setInterval(() => { console.log('Interval'); }, 1000);
タイマーの解除
設定したタイマーを解除する場合、各関数に対応した clearXxxx() の関数を呼び出します。clearTimeout(timeoutID) clearInterval(intervalID)解除関数にはタイマーの ID となるオブジェクトを渡す必要があります。 これは各設定関数の戻り値です。
前章のサンプルでは setInterval() の関数は止まりませんでしたが、 次の例では 5 秒後に止まります。
var intervalID = setInterval(() => { console.log('Interval'); }, 1000); setTimeout(() => { clearInterval(intervalID); }, 5000);ちなみに、無名関数を使わずに短く書くと次のようになります。
setTimeout(clearInterval, 5000, setInterval(console.log, 1000, 'Interval'));
非同期呼び出し setImmediate
setImmediate() は指定した関数をすぐに(Immediately) 実行します。setImmediate(func, [, arg0, arg1, ...])以下の例ではすぐにログを出力します。
setImmediate(()=>console.log('Immediate'));setImmediate() は setTimeout で 0 秒を設定したものに相当します。 ただ、相当はするのですが、 setTimeout(..., 0) と同じでもありません。
setTimeout() には制限があって、最小遅延(4,5ns 程度)が必要なので、すぐに実行したい場合には setImmediate() の方を使います。
タイムアウト時間を設定しない setImmediate() はなぜ必要なのでしょうか?
これは "すぐ" といっても、本当に "すぐ" ではないところがポイントです。
setImmediate() を実行すると、すぐに処理を返します。ここは本当にすぐです。その後、一連の処理が終わってから、指定した関数を実行します。 つまり、 関数を非同期に呼び出すことになります。
例えば、ボタンなどをクリックした後の処理ではすぐに処理を返さないと固まってしまいます。 そこで時間のかかる処理を行うことはユーザビリティーを悪化させます。
そこで、実行時間の長い処理は setImmediate() に設定して、一旦処理を返します。 ブラウザーはレイアウトや描画などの未処理の作業を処理してから、その時間のかかる処理を実行するようになります。
サンプルコード
記事で使用したサンプルコードは以下のリンクから取得(名前をつけて保存)できます。 これを node コマンドの引数に渡して実行します。$ node timer.js
Node.js API (events) - イベントシステム
Node.js の API の多くは非同期のイベント駆動構造で作成されてます。
その機能は標準モジュールの "events"
が提供しています。
今回は events モジュールのイベント駆動のシステムについて解説します。
イベント駆動の基本
Node.js では「 emitter 」(エミッター、発行者) と呼ばれるオブジェクトが用意されており、 これに イベントに関連付けて関数を登録しておくと、イベントが発生した時にその関数が実行されます。 一般的にはこの関数はコールバック関数と呼ばれますが、 Node.js では 「 listener 」(リスナー、傾聴者) と呼んでいます。例えば、 net.Socket オブジェクトではデータを受信すると
"data"
というイベントを発行します。
そのデータの受信処理の関数を登録して、受信時に呼ばれるようにします。エミッターは EventEmitter クラスのインスタンスです。 自分でエミッターを作りたい場合は EventEmitter を継承して作成します。
以下のサンプルでは ES6 の継承機能を使ってますが、 Node.js では
util.inherits
という関数も用意されています。
"use strict"; const EventEmitter = require('events'); // 継承して emitter を作成 class MyEmitter extends EventEmitter {}; var myEmitter = new MyEmitter();リスナーの登録は EventEmitter の on メソッドにイベント名とリスナーを渡します。 イベント名はキャメルケースでつけるのが慣例です。
// listener の登録 function barListener() { console.log('a foo event occurred!'); } myEmitter.on('fooEvent', barListener);イベントを発行する(イベントを発生させる)には イベント名を引数として emit メソッドを実行します。
// イベントの発行 myEmitter.emit('fooEvent'); // barListener の実行 => a foo event occurred!リスナーを実行させようとすることを call (呼び出し) ではなく、 invoke (呼びかけ) といいます。
上記は on() メソッドで登録しましたが、 once() でも登録することができます。ただ、この場合は何度イベントを発行しても、リスナーは最初の一度しか実行されません。また、後述するように非同期で実行させることもあります。
このようにリスナーは状況により実行されなかったり、後回しになったりするので、 「呼びかけ」であり、コールバックではなく「リスナー」なのではないかと思います。
リスナー
引数
イベント発生時の情報を処理側であるリスナーに渡したい場合には引数を使います。var myEmitter = new MyEmitter(); myEmitter.on('fooEvent', function(a, b) { console.log('fooEvent', a, b); }); myEmitter.emit('fooEvent', 'bar', 'baz'); // fooEvent bar baz
this
JavaScript のややこしいところに this
は何を指しているかという問題があります。リスナー実行時の
this
はイベントを発行したオブジェクトに書き換えられています。
var myEmitter = new MyEmitter(); myEmitter.on('fooEvent', function() { console.log('fooEvent this =', this); }); myEmitter.emit('fooEvent'); // fooEvent this= MyEmitter { // domain: null, // _events: { fooEvent: [Function] }, // _eventsCount: 1, // _maxListeners: undefined }ES6 のアロー関数を使うと
this
は守られます。
var myEmitter = new MyEmitter(); myEmitter.on('fooEvent', () => { console.log('fooEvent this = ', this); }); console.log('this = ', this); // this = {} myEmitter.emit('fooEvent'); // fooEvent this = {}
同期と非同期
一つのイベントに対して複数のリスナーを登録することができ、 リスナーはイベントに登録した順に実行されます。リスナーの実行はイベントが発行されるとすぐに行われる同期処理です。
var myEmitter = new MyEmitter(); myEmitter.on('fooEvent', () => console.log('1')); myEmitter.on('fooEvent', () => console.log('2')); myEmitter.on('fooEvent', () => console.log('3')); myEmitter.emit('fooEvent'); // 1 // 2 // 3イベントの発生と処理の実行を非同期にしたい場合は setImmediate() か process.nextTick() を使います。
var myEmitter = new MyEmitter(); myEmitter.on('fooEvent', () => console.log('1')); myEmitter.on('fooEvent', () => { setImmediate(() => console.log('2 (async)')); }); myEmitter.on('fooEvent', () => console.log('3')); myEmitter.emit('fooEvent'); // 1 // 3 // 2 (async)非同期にするための関数が setImmediate(Imediate : 直接の、即時の)というのはちょっと変に感じるかもしれませんが、 これは setTimeout の待ち時間 0 という意味での Immediate です。 また setImmediate() と process.nextTick() は微妙に違うらしいです。
エラーイベント
用意するイベントとそのイベント名はエミッターを作る側で自由に付けられます。 ただ、エラーが発生した場合には "error" イベントを発生させ、エラーオブジェクトを渡すのが慣例になっています。このエラーイベントが発生し、何もしていないと Node.js はエラーで落ちます。
var myEmitter = new MyEmitter(); myEmitter.emit('error', new Error('whoops!')); // events.js:141 // throw er; // Unhandled 'error' event // ^ // Error: whoops! // at Object.<anonymous> (d:h...エミッターを使う側は "error" イベントにはエラー処理のリスナーを必ず登録するのが、 ベストプラクティスとなっています。
var myEmitter = new MyEmitter(); myEmitter.on('error', (err) => { console.log('myEmitter error happned. ', err); }); myEmitter.emit('error', new Error('whoops!')); // myEmitter error happned. [Error: whoops!]
サンプルコード
記事で使用したサンプルコードは以下のリンクから取得(名前をつけて保存)できます。 これを node コマンドの引数に渡して実行します。$ node events.jsnode コマンドの使い方について詳しくは以前の記事をご覧ください。