146

catch コールバック内で単にスローし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、ブラック ホールに飲み込まれてしまうのはなぜですか?

4

7 に答える 7

172

他の人が説明したように、「ブラック ホール」は、 a の内部にスローする.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!
});

チェーンの途中でのキャッチは、ステップが失敗したにもかかわらずチェーンを続行したい場合に役立ちますが、再スローは、情報のログ記録やクリーンアップ ステップなどを行った後、おそらくどのエラーを変更した後も失敗を続ける場合に役立ちます。投げられます。

騙す

当初意図したように、エラーを Web コンソールにエラーとして表示するには、次のトリックを使用します。

.catch(function(err) { setTimeout(function() { throw err; }); });

行番号も残っているので、Web コンソールのリンクから (元の) エラーが発生したファイルと行に直接移動できます。

機能する理由

promise の履行または拒否ハンドラーとして呼び出された関数内の例外は、返されるはずの promise の拒否に自動的に変換されます。関数を呼び出す promise コードがこれを処理します。

一方、setTimeout によって呼び出される関数は、常に JavaScript の安定状態から実行されます。つまり、JavaScript イベント ループの新しいサイクルで実行されます。例外は何にもキャッチされず、Web コンソールに表示されます。err元のスタック、ファイル、行番号など、エラーに関するすべての情報を保持しているため、正しく報告されます。

于 2015-06-09T20:01:15.477 に答える
50

ここで理解しておくべき重要事項

  1. thenと関数は両方とも、catch新しい promise オブジェクトを返します。

  2. スローまたは明示的に拒否すると、現在の promise が拒否された状態に移行します。

  3. thencatchが新しい promise オブジェクトを返すため、それらを連鎖させることができます。

  4. thenpromise ハンドラー (または)内でスローまたは拒否するcatchと、連鎖パスの次の拒否ハンドラーで処理されます。

  5. jfriend00 で述べたように、thenおよびcatchハンドラは同期的に実行されません。ハンドラーがスローすると、すぐに終了します。そのため、スタックが巻き戻され、例外が失われます。そのため、例外をスローすると現在の約束が拒否されます。


あなたの場合、オブジェクトdo1を投げて内部を拒否していErrorます。これで、現在の promise は拒否された状態になり、制御は次のハンドラーに移されthenます。

thenハンドラーには拒否ハンドラーがないため、do2はまったく実行されません。これは、その中で使用することで確認できますconsole.log。現在の promise には拒否ハンドラーがないため、前の promise からの拒否値で拒否され、次のハンドラーである に制御が移されcatchます。

catch拒否ハンドラーと同様に、そのconsole.log(err.stack);内部で行うと、エラー スタック トレースを確認できます。ここで、そこからオブジェクトをスローErrorしているため、返される promisecatchも拒否された状態になります。

に拒否ハンドラをアタッチしていない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] }

ハンドラー 1内では、拒否されたオブジェクトcatchの値を取得しています。promise

同様に、ハンドラー 1 によって返された promisecatchも、拒否されたのと同じエラーで拒否され、2 番目のハンドラーpromiseでそれを観察しています。catch

于 2015-06-08T17:37:30.813 に答える
1

.catchはい、エラーを飲み込むことを約束します。他の回答で詳しく説明されているように、でのみキャッチできます。Node.js を使用していて、通常のthrow動作を再現し、スタック トレースをコンソールに出力してプロセスを終了する場合は、次のことができます。

...
  throw new Error('My error message');
})
.catch(function (err) {
  console.error(err.stack);
  process.exit(0);
});
于 2015-11-07T00:00:29.933 に答える