解決後もJavaScript ES6プロミスが実行を続けるのはなぜですか?


97

promiseはresolve()またはreject()を実行できるものであることを理解しているので、resolveまたはrejectが呼び出された後も、promiseのコードが実行され続けることに驚いた。

すべての即時の関数実行を停止する、exitまたはreturnの非同期フレンドリーバージョンであることを解決または拒否することを検討しました。

次の例でresolveの呼び出し後にconsole.logが表示されることがある理由を誰かが説明できますか。

var call = function() {
    return new Promise(function(resolve, reject) {
        resolve();
        console.log("Doing more stuff, should not be visible after a resolve!");
    });
};

call().then(function() {
    console.log("resolved");
});

jsbin


12
合理的な質問ですが、JSは、指示されたように、次のステートメントを1つずつ実行します。resolve()魔法のようにの効果を持つJS制御ステートメントではありませんreturn。これは単なる関数呼び出しであり、はい、実行はその後も続行されます。

これは良い質問です。すべての回答を読んでも、ベストプラクティスについては
ガブリエルグレン

誤解は、resolve()で終了していることに起因すると思います:resolve()を呼び出した直後に約束は解決されますが、これは他の人がすでに言ったように、約束を終了させた関数が義務も、それが「正常な」終了に達するまで続く。
ジュゼッペベルトーネ

回答:


143

JavaScriptには、「最後まで実行」という概念があります。エラーがスローされない限り、関数はreturnステートメントまたはその終わりに達するます。関数の外部の他のコードは、それを妨害することはできません(エラーがスローされない限り)。

あなたがしたい場合はresolve()、あなたの初期化子の機能を終了するには、次の方法でそれを付加する必要がありますreturn

return new Promise(function(resolve, reject) {
    return resolve();
    console.log("Not doing more stuff after a return statement");
});

こんにちはFelix-これはストーリーの一部に過ぎないと思います-他の部分resolve()はそれ自体が非同期関数です。他の(削除された)回答で見たように、一部の人々は、呼び出しresolveはすぐにコールバックを実行すると信じています。
Alnitak 2015年

3
@Alnitak resolve自体は非同期ではなく、完全に同期しています。厳密にES6 APIを使用していますが、同期か非同期かは監視できません。
エサイリヤ2015年

1
@Esailijaわかりました、おそらく私は不明確でした。一部の人々は、呼び出しresolveによって登録されたコールバックがすぐに呼び出され、現在のコールスタックの一部になると考えています。その代わりに、それだけでコールバックをキューに入れ、真実ではない(とあなたしている権利は、それは非同期ではないが、それだけでそのことをして、すぐに終了)
オリオン座ゼータ星

@アルニタク:私はあなたが言っていることを理解しています。console.logなぜこの順序で表示されるのではなく、なぜ表示されるのかを解釈しただけです。これまでのところ、何resolveをどのように約束するかは、私が質問をどのように解釈するかとは無関係です。しかしもちろん、約束の文脈で知ることは依然として重要です。私があなたの答えを支持した理由の1つ:)
Felix Kling

9
@Bergi、あなたの編集で、あなたは「return resolve();」と言います。珍しいようです。ここで重要なことは何もないことを納得させるために、ドキュメントを読んで、(1)resolve()が結果を返さないように見えること、(2)初期化コールバックの戻り値が返らないことを確認する必要がありました。使用されているようです。「resolve(); return;」と言ったほうが明確ではないでしょうか。それにより、この気晴らしを回避しますか?
Don Hatch

19

resolveプロミス時に呼び出されるコールバックは、仕様で非同期に呼び出される必要があります。これは、同期アクションと非同期アクションの混合に対してpromiseを使用するときに一貫した動作を保証するためです。

したがってresolve、コールバックを呼び出すとqueuedになり、関数の実行はresolve()呼び出しに続くコードで直ちに続行されます。

JSイベントループに制御が戻されると、コールバックをキューから削除して実際に呼び出すことができます。


1
コールバックキューイングはA +仕様またはES6で文書化されていますか?
thefourtheye 2015年

5
@thefourtheye:イベントループの仕様は、実際には現在HTML5の一部です。ES6は、EnqueueJobによって呼び出されると呼ばれる内部メソッドを定義し.thenます。
Felix Kling、2015年

@thefourtheye:実際には、ES6もキューを定義しているようです:people.mozilla.org/~jorendorff/…。イベントループに関連していると思います。
Felix Kling 2015

@FelixKlingリンクに感謝-これがどのように機能するかはわかっていましたが、章と詩を引用することはできませんでした
Alnitak

2
@FelixKlingマイクロタスク/マクロタスクです。実行中の実行コンテキストがなく、実行コンテキストスタックが空の場合、ECMAScript実装は最初のPendingJobをジョブキューから削除し、含まれている情報を使用します。実行コンテキストを作成し、関連するジョブの抽象操作の実行を開始します。」
Benjamin Gruenbaum 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.