Posted at

Uncaught (in promise)の発生条件と抑制方法

More than 1 year has passed since last update.

同期にせよ非同期にせよ、普通にエラーをthrowしてcatchしないでおくとブラウザの開発者ツールのコンソールにエラーとして表示されます。

setTimeout(function() {throw new Error('async')}, 1000);

throw new Error('sync');

// Uncaught Error: sync
// Uncaught Error: async 1秒後

Promiseのエラーは実際はrejectなので、本質的にはErrorがthrowされた状態ではありません。が、親切なコンソールではエラーハンドルを忘れていた場合(promiseがrejectされるまでに、一回もcatchハンドラーが設定されていなかった場合)に以下のように表示されます。

親切なコンソール(FirefoxやChrome)

Promise.reject(new Error('promise!!!'));

// Uncaught (in promise) Error: promise!!!

これは開発中にうっかりcatchし忘れるのを防ぐのに大変便利ですので、基本的に抑制すべきではありません。


しかし、1パターンだけわかっていて抑制したい場合があります。それは「今このpromiseはrejectするが、catchハンドラーがsetされるのはもっと後」という場合です。

var promise = Promise.reject(new Error('promise!!!'));

setTimeout(function() {promise.catch(/* 適切なエラーハンドル */);}, 1000);

// Uncaught (in promise) Error: promise!!!
// 後々エラーハンドルするんだけど、コンソールにはUncaught Errorが出てしまう!

これは「promiseがrejectされるまでに、一回もcatchハンドラーが設定されていなかった場合」に表示されるのですから、以下のように即座に一回promiseをcatchしてしまえば抑制できます。

var promise = Promise.reject(new Error('promise!!!'));

// これで抑制できる!!(後で見る人にとっては意味不明なのでコメントでこの記事のURLを貼っておこう)
promise.catch(function(e) {});

setTimeout(function() {promise.catch(/* 適切なエラーハンドル */);}, 1000);

// なにもでない

なお、似てるようですが下記の書き方は大変まずいです。

var promise = Promise.reject(new Error('promise!!!')).catch(function(e) {});

setTimeout(function() {promise.catch(/* 適切なエラーハンドル…でcatchできない!!! */);}, 1000);

// なにもでない

これだと変数promiseには「エラーをすべて握りつぶした後のpromise」が登録されてしまいます。後続のエラーハンドルが全く機能しなくなってしまうので、絶対にやめましょう。