nodejs
差分
このページの2つのバージョン間の差分を表示します。
両方とも前のリビジョン前のリビジョン次のリビジョン | 前のリビジョン | ||
nodejs [2015/11/06 05:54] – [callbackと例外のアンチパターン] nullpon | nodejs [2016/10/13 02:30] (現在) – [--max-old-space-size] nullpon | ||
---|---|---|---|
行 2: | 行 2: | ||
[[http:// | [[http:// | ||
- | ===== tips ===== | + | ===== バージョンとES feature |
- | ==== callbackと例外のアンチパターン ==== | + | ブラウザと違って互換性を気にする必要はないので最新機能は遠慮なく使いましょう |
+ | |||
+ | node v4 | ||
+ | |||
+ | * デフォルト引数、分割代入、import以外のES2015機能が使えます。 | ||
+ | |||
+ | node v6 | ||
+ | |||
+ | * import以外のES2015機能が使えます。 | ||
+ | |||
+ | ===== callbackに関するtips ===== | ||
+ | |||
+ | ==== callbackの第一引数はerrにする ==== | ||
+ | |||
+ | 絶対にエラーの発生しない処理だとしてもcallbackの第一引数はエラーが渡される想定のコードにすること。第一引数にnullを渡してcallbackを呼べし。 | ||
+ | |||
+ | node, npmの多くのライブラリは第一引数でエラーを受け取る事を前提にしているので、こうすることで各種ライブラリ、モジュールとスムーズに連携できるようになる。 | ||
+ | |||
+ | <code javascript> | ||
+ | // これはだめ | ||
+ | function asyncFunc(callback) { | ||
+ | | ||
+ | callback(true); | ||
+ | }); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <code javascript> | ||
+ | // OK | ||
+ | function asyncFunc(callback) { | ||
+ | | ||
+ | callback(null, | ||
+ | }); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== 非同期処理には必ずcallbackを ==== | ||
+ | |||
+ | 呼び出し元に結果を通知する必要のない非同期処理でも必ずcallbackを付ける(PromiseかEvent駆動ならcallbackなし可) | ||
+ | |||
+ | bad | ||
+ | |||
+ | <code javascript> | ||
+ | function writeLogToDB(code, | ||
+ | // 失敗しても良い処理なので、呼び出しもとにエラーを通知しなくてもいい (← ダメです。だいたい後で通知したくなります) | ||
+ | | ||
+ | err && mainLogger.error(err); | ||
+ | }); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | good | ||
+ | |||
+ | <code javascript> | ||
+ | function writeLogToDB(code, | ||
+ | // callbackが渡されてなければデフォルトの処理を入れる | ||
+ | if (!callback) callback = function(err) { | ||
+ | err && mainLogger.error(err); | ||
+ | } | ||
+ | |||
+ | | ||
+ | callback(err); | ||
+ | }); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== callbackを2度呼ばない | ||
< | < | ||
行 18: | 行 85: | ||
</ | </ | ||
- | try側のcallback中に例外が発生するとcatch側のcallbackが呼ばれるので2回呼ばれてしまう | + | try側のcallback中に例外が発生するとcatch側のcallbackが呼ばれるので2回呼ばれてしまう。これによってデータ2重書き込み等の重大な問題が発生する恐れがあります。 |
- | ==== requireのアンチパターン | + | ===== require |
- | requireされるファイルは関数やクラスの定義のみにする。読み込んだだけでファイルの書き込み読み込み、サーバの起動、DB接続その他の処理を行うjsはだめ。 | + | ==== require時に処理を走らせない ==== |
+ | |||
+ | requireされるファイルは関数やクラスの定義のみにする。読み込んだだけでファイルの書き込み読み込み、サーバの起動、DB接続その他の処理を行ってはいけない。 | ||
Good | Good | ||
- | < | + | < |
// hoge.js | // hoge.js | ||
var hoge = module.exports; | var hoge = module.exports; | ||
行 34: | 行 103: | ||
} | } | ||
- | // main.js | ||
- | var hoge = require(" | + | // main.js |
- | hoge.init(); | + | var hoge = require(" |
+ | hoge.init(); | ||
</ | </ | ||
Bad | Bad | ||
- | < | + | < |
// hoge.js | // hoge.js | ||
var hoge = module.exports; | var hoge = module.exports; | ||
行 50: | 行 119: | ||
} | } | ||
- | hoge.init(); | + | hoge.init(); |
- | // main.js | ||
- | require(" | + | // main.js |
+ | require(" | ||
</ | </ | ||
requireされるだけで何らかの処理が走るようにすると、起動時に予期せぬタイミングでinitが実行されたり、テストコードでinitが不要なのに呼ばれてしまったりする問題が発生します。 | requireされるだけで何らかの処理が走るようにすると、起動時に予期せぬタイミングでinitが実行されたり、テストコードでinitが不要なのに呼ばれてしまったりする問題が発生します。 | ||
+ | ===== メモリ ===== | ||
+ | ==== --max-old-space-size ==== | ||
+ | < | ||
+ | デフォルトで1.5GB以上のメモリを確保しようとするとクラッシュします。ひたすらメモリを食いつぶしていくコードを実行すると… | ||
+ | ``` | ||
+ | <--- Last few GCs ---> | ||
- | ===== HTML5 の Server-Sent Events を実装してみる ===== | + | 84313 ms: Mark-sweep 1389.5 (1456.5) -> 1396.1 (1456.5) MB, 844.5 / 0 ms [allocation failure] [GC in old space requested]. |
+ | 85167 ms: Mark-sweep 1396.1 (1456.5) -> 1398.0 (1456.5) MB, 854.0 / 0 ms [allocation failure] [GC in old space requested]. | ||
+ | 86028 ms: Mark-sweep 1398.0 (1456.5) -> 1387.1 (1456.5) MB, 860.4 / 0 ms [last resort gc]. | ||
+ | 86898 ms: Mark-sweep 1387.1 (1456.5) -> 1389.0 (1456.5) MB, 870.4 / 0 ms [last resort gc]. | ||
- | [[http:// | ||
- | サーバ側を node.js で実装してみる。 | + | <--- JS stacktrace ---> |
- | generator.js | + | ==== JS stack trace ========================================= |
- | <code javascript> | + | |
- | // EventEmitterを取得 | + | |
- | var events | + | |
- | var emitter | + | |
- | // EventEmitterを拡張 | + | Security context: 0x2e7afd4b4629 <JS Object> |
- | emitter.id = 0; | + | 1: nextTick [node.js:~473] [pc=0x1cfdf0766f8e] (this=0x3bb898d1c211 <a process with map 0x31a25a8113b9>, |
- | emitter.data | + | |
- | emitter.start | + | |
- | | + | |
- | | + | |
- | setInterval(function() { | + | |
- | var pos = Math.floor(Math.random() * chars.length * 2); | + | |
- | if (pos < chars.length) { | + | |
- | self.id++; | + | |
- | self.data | + | |
- | // generatedイベントとして登録されたリスナを呼び出す | + | |
- | self.emit(" | + | |
- | } | + | |
- | }, 1000); | + | |
- | }; | + | |
- | // module.exportsに設定されたオブジェクトがrequireの戻り値となる。 | + | FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory |
- | module.exports = emitter; | + | zsh: abort node hoge.js |
- | </ | + | ``` |
- | server.js | + | 上限を増やすには |
- | <code javascript> | + | |
- | var http = require(" | + | |
- | http.createServer(handler).listen(8124); | + | |
- | var generator | + | ``` |
- | generator.start(); | + | $ node --max-old-space-size=2048 hoge.js |
+ | ``` | ||
- | function handler(req, | + | これを実行すると… |
- | function response(id, | + | ``` |
- | | + | <--- Last few GCs ---> |
- | + | ||
- | res.writeHead(200, | + | |
- | " | + | |
- | }); | + | |
- | res.write(" | + | |
- | res.write(" | + | |
- | res.write(" | + | |
- | res.end(); | + | |
- | + | ||
- | } | + | |
- | generator.on(" | + | |
- | } | + | |
- | </code> | + | |
- | クライアント側のJavaScript | + | 183271 ms: Scavenge 2044.5 (2114.7) -> 2044.5 (2114.7) MB, 0.8 / 0 ms (+ 1.4 ms in 1 steps since last GC) [allocation failure] [incremental marking delaying mark-sweep]. |
- | <code javascript> | + | 184563 ms: Mark-sweep 2044.5 (2114.7) -> 2035.4 (2114.7) MB, 1291.3 / 0 ms (+ 2.3 ms in 2 steps since start of marking, biggest step 1.4 ms) [last resort gc]. |
- | var source | + | 185914 ms: Mark-sweep 2035.4 (2114.7) -> 2036.5 (2114.7) MB, 1351.8 / 0 ms [last resort gc]. |
- | source.onmessage | + | |
- | // データ受信時の処理を書く | + | |
- | // event.data でサーバから送られてきたデータを取得できる | + | <--- JS stacktrace ---> |
- | } | + | |
- | </code> | + | ==== JS stack trace ========================================= |
+ | |||
+ | Security context: 0x1f9ab28b4629 <JS Object> | ||
+ | 1: $toString(aka ToString) [native runtime.js:~563] [pc=0x3fdefe2641cb] | ||
+ | 2: a(aka a) [/Users/nullpon/ | ||
+ | | ||
+ | |||
+ | FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory | ||
+ | zsh: abort node --max-old-space-size=2048 hoge.js | ||
+ | ``` | ||
+ | 今度は2GBまで持ちました。が、こんなにメモリを使う実装の前に色々直しましょう | ||
+ | </ |
nodejs.1446789246.txt.gz · 最終更新: 2015/11/06 05:54 by nullpon