技術と魚

技術調査、開発TIPS、駄文

Promiseのスタックトレースが分かりにくい時はbluebirdが使える

JavaScriptのPromiseによる非同期を経由すると、エラー発生時に非同期の呼び出し元以前のスタックトレースが出力されないので、デバッグに困ることがあった。

けど調べてみたらbluebird*1のlong stack trace機能を使えばそれも含めて出してくれるっぽいということで、試してみた。

bluebirdjs.com

上記を読む限り、nodejsでやる場合は NODE_ENV=development の環境変数で実行する必要があるらしい。 WebpackのproductionではlongStackTraceをfalseにすることを推奨しているということは、おそらくパフォーマンス的には多少影響するということだろう。

というわけで、以下のようなコードを実行する

var Promise = require('bluebird');

const defer = () => new Promise((resolve) => setTimeout(() => resolve(), 0))

const example1 = () =>
  defer()
    .then(() => {
      throw new Error('failed');
    });

example1()
  .catch((e) => {
    console.error(e);
  });

bluebird使用、long stack trace=OFFの場合

$ node sample.js
Error: failed
    at defer.then (/Users/shun/repos/bluebird-sample/sample.js:10:13)
    at tryCatcher (/Users/shun/repos/bluebird-sample/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/Users/shun/repos/bluebird-sample/node_modules/bluebird/js/release/promise.js:517:31)
    at Promise._settlePromise (/Users/shun/repos/bluebird-sample/node_modules/bluebird/js/release/promise.js:574:18)
    at Promise._settlePromise0 (/Users/shun/repos/bluebird-sample/node_modules/bluebird/js/release/promise.js:619:10)
    at Promise._settlePromises (/Users/shun/repos/bluebird-sample/node_modules/bluebird/js/release/promise.js:699:18)
    at _drainQueueStep (/Users/shun/repos/bluebird-sample/node_modules/bluebird/js/release/async.js:138:12)
    at _drainQueue (/Users/shun/repos/bluebird-sample/node_modules/bluebird/js/release/async.js:131:9)
    at Async._drainQueues (/Users/shun/repos/bluebird-sample/node_modules/bluebird/js/release/async.js:147:5)
    at Immediate.Async.drainQueues (/Users/shun/repos/bluebird-sample/node_modules/bluebird/js/release/async.js:17:14)
    at runCallback (timers.js:810:20)
    at tryOnImmediate (timers.js:768:5)
    at processImmediate [as _immediateCallback] (timers.js:745:5)

bluebird使用、long stack trace=ONの場合

$ NODE_ENV=development node sample.js
Error: failed
    at defer.then (/Users/shun/repos/bluebird-sample/sample.js:10:13)
    at runCallback (timers.js:810:20)
    at tryOnImmediate (timers.js:768:5)
    at processImmediate [as _immediateCallback] (timers.js:745:5)
From previous event:
    at example1 (/Users/shun/repos/bluebird-sample/sample.js:9:6)
    at Object.<anonymous> (/Users/shun/repos/bluebird-sample/sample.js:13:1)
    at Module._compile (module.js:653:30)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)
    at Function.Module.runMain (module.js:694:10)
    at startup (bootstrap_node.js:204:16)
    at bootstrap_node.js:625:3

bluebirdを使わない、デフォルトのPromiseの場合

# requireをコメントアウトして実行
$ node sample.js
Error: failed
    at defer.then (/Users/shun/repos/bluebird-sample/sample.js:8:13)
    at <anonymous>

long stack traceを使うと From previous event というフレーズで非同期呼び出し元以前を辿れるのがわかる。

*1:bluebirdはPromiseの実装の一つ。