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仕様の話はまだない