キャッチコールバック内にError
をスローして、プロセスが他のスコープ内にあるかのようにエラーを処理できないのはなぜですか?
console.log(err)
を実行しないと、何も出力されず、何が起こったのか何もわかりません。プロセスは終了しました...
例:
function do1() {
return new Promise(function(resolve, reject) {
throw new Error('do1');
setTimeout(resolve, 1000)
});
}
function do2() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject(new Error('do2'));
}, 1000)
});
}
do1().then(do2).catch(function(err) {
//console.log(err.stack); // This is the only way to see the stack
throw err; // This does nothing
});
メインスレッドでコールバックが実行される場合、なぜError
がブラックホールに飲み込まれますか?
他の人が説明したように、「ブラックホール」は、.catch
の内部へのスローが拒否されたプロミスでチェーンを継続し、エラーを飲み込む未終了のチェーンにつながるキャッチがないためです(悪い!)
もう1つキャッチを追加して、何が起こっているのかを確認します。
do1().then(do2).catch(function(err) {
//console.log(err.stack); // This is the only way to see the stack
throw err; // Where does this go?
}).catch(function(err) {
console.log(err.stack); // It goes here!
});
チェーンの途中でのキャッチは、失敗したステップにもかかわらずチェーンを続行したい場合に役立ちますが、再スローは、情報のログ記録やクリーンアップなどを行った後にcontinue failingに役立ちますおそらく、どのエラーがスローされるかを変更します。
最初に意図したとおり、Webコンソールにエラーとしてエラーを表示するには、次のトリックを使用します。
.catch(function(err) { setTimeout(function() { throw err; }); });
行番号も残っているため、Webコンソールのリンクを使用すると、(元の)エラーが発生したファイルと行に直接移動できます。
約束履行または拒否ハンドラーと呼ばれる関数の例外は、返されるはずの約束の拒否に自動的に変換されます。関数を呼び出すプロミスコードがこれを処理します。
一方、setTimeoutによって呼び出される関数は、常にJavaScriptの安定状態から実行されます。つまり、JavaScriptイベントループの新しいサイクルで実行されます。そこにある例外は何にもとらわれず、Webコンソールに到達します。 err
は、元のスタック、ファイル、行番号など、エラーに関するすべての情報を保持しているため、依然として正しく報告されます。
ここで理解すべき重要な事項
then
関数とcatch
関数は両方とも、新しいpromiseオブジェクトを返します。
スローまたは明示的に拒否すると、現在のプロミスが拒否された状態に移行します。
then
およびcatch
は新しいpromiseオブジェクトを返すため、これらをチェーン化できます。
Promiseハンドラー(then
またはcatch
)内でスローまたは拒否した場合、チェーンパスの次の拒否ハンドラーで処理されます。
Jfriend00で述べたように、then
およびcatch
ハンドラーは同期的に実行されません。ハンドラーがスローすると、すぐに終了します。したがって、スタックは巻き戻され、例外は失われます。そのため、例外をスローすると現在の約束が拒否されます。
あなたの場合、Error
オブジェクトをスローすることでdo1
内で拒否しています。これで、現在のプロミスは拒否状態になり、コントロールは次のハンドラー(この場合はthen
)に転送されます。
then
ハンドラーには拒否ハンドラーがないため、do2
はまったく実行されません。これを確認するには、console.log
を使用します。現在のプロミスには拒否ハンドラがないため、前のプロミスの拒否値で拒否され、コントロールはcatch
である次のハンドラに転送されます。
catch
は拒否ハンドラであるため、その中でconsole.log(err.stack);
を実行すると、エラースタックトレースを確認できます。現在、そこからError
オブジェクトをスローしているので、catch
によって返されるpromiseも拒否状態になります。
catch
に拒否ハンドラをアタッチしていないため、拒否を監視することはできません。
このように、チェーンを分割してこれをよりよく理解できます
var promise = do1().then(do2);
var promise1 = promise.catch(function (err) {
console.log("Promise", promise);
throw err;
});
promise1.catch(function (err) {
console.log("Promise1", promise1);
});
あなたが得る出力は次のようなものになります
Promise Promise { <rejected> [Error: do1] }
Promise1 Promise { <rejected> [Error: do1] }
catch
ハンドラー1内では、拒否されたpromise
オブジェクトの値を取得しています。
同様に、catch
ハンドラー1によって返されたプロミスも、promise
が拒否されたのと同じエラーで拒否され、2番目のcatch
ハンドラーでそれを観察しています。
上記のsetTimeout()
メソッドを試しました...
.catch(function(err) { setTimeout(function() { throw err; }); });
迷惑なことに、これは完全にテスト不可能であることがわかりました。非同期エラーがスローされるため、catch
はエラーがスローされるまでにリッスンを停止するため、try/catch
ステートメント内にラップすることはできません。
完全に機能するリスナーを使用するだけに戻りました。これは、JavaScriptの使用方法であるため、非常にテスト可能でした。
return new Promise((resolve, reject) => {
reject("err");
}).catch(err => {
this.emit("uncaughtException", err);
/* Throw so the promise is still rejected for testing */
throw err;
});
d。呼び出して例外eをスローした場合、
a。 resolvePromiseまたはrejectPromiseが呼び出されている場合は、無視してください。
b。それ以外の場合は、eを理由として約束を拒否します。
つまり、then
関数で例外をスローすると、キャッチされ、約束が拒否されます。 catch
はここでは意味がありません。これは.then(null, function() {})
への単なるショートカットです
コードに未処理の拒否を記録する必要があると思います。ほとんどのPromiseライブラリは、unhandledRejection
を起動します。ここに 関連する要点 とそれに関する議論があります。
私はこれが少し遅いことを知っていますが、私はこのスレッドに出くわしました、そして、どのソリューションも私にとって簡単に実装できなかったので、私は自分のものを思いつきました:
次のように、promiseを返す小さなヘルパー関数を追加しました。
function throw_promise_error (error) {
return new Promise(function (resolve, reject){
reject(error)
})
}
次に、エラーをスローする(およびプロミスを拒否する)約束チェーンの特定の場所がある場合、構築されたエラーを使用して上記の関数から単純に戻ります。
}).then(function (input) {
if (input === null) {
let err = {code: 400, reason: 'input provided is null'}
return throw_promise_error(err)
} else {
return noterrorpromise...
}
}).then(...).catch(function (error) {
res.status(error.code).send(error.reason);
})
このように、私はプロミスチェーンの内部から余分なエラーを投げることを制御しています。 「通常の」promiseエラーも処理する場合は、キャッチを拡張して「自己スロー」エラーを個別に処理します。
これが役立つことを願って、それが私の最初のstackoverflowの答えです!
はいは、飲み込みエラーを約束し、他の回答で詳細に説明されているように、.catch
でのみキャッチできます。 Node.jsで、通常のthrow
の動作を再現し、スタックトレースをコンソールに出力してプロセスを終了する場合は、次のようにします。
...
throw new Error('My error message');
})
.catch(function (err) {
console.error(err.stack);
process.exit(0);
});