Promiseの使い方が良く分からなくなってきた
先に言っておくと疑問文で終わる雑な記事です。
Promise、正常系の処理は比較的簡単に書けるのだけど、何か間違いがあったときに Promise 自体のエラーハンドリングが強力すぎて良く分からなくなる。
readFile
というファイルを読み込む Promise ベースの良くできた関数があるとして、
読み込んだファイルを処理するとき
readFile("foo.txt").then(function(text) { doSomething(text); });
ファイルが読み込めない場合を想定するとき
readFile("not-found.txt").then(function(text) { doSomething(text); }, function() { readFailed(); });
この時、catch
を使うのは良くなくて、ファイルの読み込みは成功したのに readFailedが実行される場合が考えられる。
readFile("存在するファイル.txt").then(function(text) { doBrokenSomething(text); // ここでエラーが発生する }).catch(function() { readFailed(); });
これを回避するにはこう書く。というか Promise ベースの API を使ったら最後に必ず catch
しておく必要があると思う。
readFile("foo.txt").then(function(text) { doComplexSomething(text); // ここでエラーが発生するかも知れない }, function() { readFailed(); }).catch(console.error.bind(console)); // ^^ doComplexSomething/ readFailed でエラーがあったとき
と、いうのが Promise の基本的な使い方だと思っているのだけど、、
これだとエラーがあったことは分かるけど、どこでエラー発生したか等の情報がいまいち分かりにくくて、デバッグしづらい。よくよく考えると Promise は非同期で実行されるフローを分かりやすく書くことが目的なので、すべてのおぜん立てが揃った最後の then
の中で複雑な処理を書くのがそもそもの間違いな気がする。その段階では Promise のエラーハンドリング機能は邪魔でしかないと思う。Promise の呪縛から逃れるには 大きなスコープ を使うか setTimeout
を使うというのが考えられるのだけど、本当にこんなので良いのでしょうか?
// 大きなスコープを使う var text = null; readFile("foo.txt").then(function(_text) { text = _text; }); button.on("click", function() { if (text !== null) { doComplexSomething(text); } else { readFailed(); } });
// setTimeout を使う button.on("click", function() { readFile("foo.txt").then(function(text) { setTimeout(function() { doComplexSomething(text); }, 0); }, function() { setTimeout(function() { readFailed(); }, 0); }); });
追記:
ということをブツブツ書いていたら、有益な情報教えていただきました!!
@mohayonao > 回避するのに then の中で setTimeout
Promise#done的なやつですね。
エラーの握りつぶし(unhandledRejection)が起きやすい問題は今実装進んでる最中という感じです
https://t.co/Yylri9lYEw
— azu (@azu_re) 2015, 3月 3