Promise Error Handling
約束されたエラー
Promiseとエラーの基本
- Promise内で起きた例外は自動でキャッチされる
- エラー処理は.catch(fn)で行う
var promise = new Promise(function(){
    throw new Error("例外");
});
promise.catch(function(error){
    // 例外をキャッチできる
});
よくある問題
- .catch(fn)をしないとエラーログも出せない
- .catch(fn)をしわすれてエラーの握りつぶしが起きる
- = unhandled rejection (.catchをしてないpromise)
- 4.6. Promise.prototype.done とは何か?
現状のunhandled rejectionへの対応
- unhandled rejectionが発生した時にコンソールに出すかは実装依存
- FirefoxはGCのタイミング
- Chromeは開発者ツールが有効の場合
- bluebirdやypromiseはコンソールへ出す
- Node.jsは何も言わない?
今日のテーマ: unhandledRejection
- unhandled rejectionは予期せぬ出来事
- 少なくてもエラーのログは取りたい- Promise.prototype.doneを実装するのは本質的ではない
 
- 'unhandledRejection',- 'rejectionHandled'というイベントの実装が進められているという話
- ECMAScript 6の仕様ではありません
unhandledRejection / rejectionHandled
例はbluebird
unhandledRejection イベント
var bluebird = require("bluebird");
process.on("unhandledRejection", function (reason, promise) {
    console.log("unhandledRejection");
});
var resolved = bluebird.resolve();
resolved.then(function () {
    throw new Error("Yay!");
});
unhandledRejection イベント
- catchしてないPromiseでエラーが発生すると発行されるイベント
- .catchしてないpromiseオブジェクトを見つけるのを助ける
- window.onerrorみたいなもの
process.on("unhandledRejection", function (reason, promise) {
    // エラー理由とpromiseがやってくる
});
rejectionHandled イベント
var Promise = require("bluebird");
process.on("rejectionHandled", function (promise) {
    console.log("rejectionHandled");
});
var rejected = Promise.reject(new Error("Error Promise"));
setTimeout(function () {
    rejected.catch(function () {
        // rejected済みのpromiseに`catch`する
    });
},100);
rejectionHandled イベント
- rejected済みのpromiseにcatchした時に起きるイベント
- 呼ばれることがないcatchの発見に役立つ
実際の使い方
- unhandledRejectionのログを取りたい場合
var unhandledRejections = new Set();
process.on('unhandledRejection', function(reason, p) {
  unhandledRejections.add(p);
});
process.on('rejectionHandled', function(p) {
  unhandledRejections.delete(p);
});
unhandledRejection & rejectionHandled
- なぜunhandledRejectionだけ欲しいのにrejectionHandledも見るの?
- => rejectionHandledが起きるケースはunhandledRejectionが先に起きてる事がある
unhandledRejection & rejectionHandledパータン
var rejected = Promise.reject();
setTimeout(()=>{
    // 2. rejectionHandledイベント
    rejected.catch(()=>{});
}, 100);
// 1. unhandledRejectionイベント
unhandledRejection & rejectionHandled
- unhandledRejection と rejectionHandled は基本セットで使う
- rejectionHandled単体の使い道はあんまりなさそう?
使い方とドキュメント
参考資料
- process io.js Manual & Documentation
- Promise unhandled rejection tracking global handler hook
- Global rejection events - bluebird
実装
ことのはじまり
- bluebirdの実装提案
- @benjamingrさんが色々利用状況を調べてプロポーサルを書いた
- Promise unhandled rejection tracking global handler hook
ライブラリの実装
- bluebird v2.7.0で実装
- when.js v3.7.0で実装
- io.js v1.4.1で実装 by @petkaantonov
小さいプロポーサルからの実装
- Promise/A+の頃から同じような話はあった
- DOM/ECMAScript Promiseでも話があった程度
Implementation in userland
Implementation in userland -- Consider exposing promise unhandled rejection hook · Issue #256 · iojs/io.js
Implementation in userland
- ユーザランドでの実装から始まっている面白い動き- Promise自体もコミュニティ仕様からECMAScript仕様に入った
 
- io.js にも入ったため、他のPromiseライブラリにも実装が進んでいきそうな空気がある- コミュニティ標準から仕様へ?
 
まとめ
- Promiseでエラーの握りつぶしがよく起きてる
- 現状ではunhandled rejectionの扱いは実装依存
- unhandled rejectionが起きた時に発行するイベントを定義したコミュニティプロポーサルがでた
- bluebirdやio.jsなどで実装された
- ECMAScript仕様の話はまだない