[解決済み] ジャバスクリプトのプロミス好奇心
質問
このプロミスを呼び出すと、出力が関数呼び出しのシーケンスと一致しない。その
.then
の前に
.catch
を持つ約束でも
.then
が後に呼び出されていたにもかかわらずです。その理由は何でしょうか?
const verifier = (a, b) =>
new Promise((resolve, reject) => (a > b ? resolve(true) : reject(false)));
verifier(3, 4)
.then((response) => console.log("response: ", response))
.catch((error) => console.log("error: ", error));
verifier(5, 4)
.then((response) => console.log("response: ", response))
.catch((error) => console.log("error: ", error));
出力
node promises.js
response: true
error: false
どのように解決するのですか?
これはちょっとクールな質問で、真相を知りたいですね。
これをやると
verifier(3,4).then(...)
を実行する前に、イベントループに戻る必要があります。
.catch()
ハンドラを実行する前に、イベントループに戻る必要があります。 その余分なサイクルが次のシーケンスを与える。
verifier(5,4).then(...)
を実行する機会があります。
.then()
ハンドラの前に
.catch()
の前にすでにキューに入っていたからです。
.catch()
のハンドラがキューに入る前に既にキューに入っており、アイテムは FIFO 順でキューから実行されるからです。
注意点として、もし
.then(f1, f2)
の代わりに
.then().catch()
の代わりに
const verifier = (a, b) =>
new Promise((resolve, reject) => (a > b ? resolve(true) : reject(false)));
verifier(3, 4)
.then((response) => console.log("response (3,4): ", response),
(error) => console.log("error (3,4): ", error)
);
verifier(5, 4)
.then((response) => console.log("response (5,4): ", response))
.catch((error) => console.log("error (5,4): ", error));
なお、すべてのメッセージにラベルをつけましたので、どの
verifier()
の呼び出しがわかるように、すべてのメッセージにラベルをつけました。
ES6 Spec on promise callback ordering とより詳細な説明。
ES6仕様では、プロミスの "jobs"(コールバックは
.then()
または
.catch()
) は、ジョブキューに挿入された時間に基づいて、FIFO 順で実行されます。 FIFOという具体的な名前は出てきませんが、新しいジョブはキューの最後に挿入され、ジョブはキューの先頭から実行されることが指定されています。 これはFIFOの順序付けを実装しています。
PerformPromiseThen
(からのコールバックを実行します。
.then()
につながる。
EnqueueJob
これは、解決または拒否ハンドラが実際に実行されるようスケジュールされる方法です。EnqueueJobは、保留中のジョブがジョブキューの後ろに追加されることを指定します。次に
NextJob
オペレーションはキューの先頭から項目を引き出します。これにより、Promiseジョブキューからのジョブを処理する際のFIFO順序が保証されます。
つまり、元の質問の例では、コールバックは
verifier(3,4)
プロミスと
verifier(5,4)
のプロミスが実行された順番にジョブキューに挿入されます。なぜなら、それらのオリジナルのプロミスは両方とも終了しているからです。 そして、インタプリタがイベントループに戻ると、最初に
verifier(3,4)
ジョブをピックアップします。 そのプロミスは拒否され、そのためのコールバックは
verifier(3,4).then(...)
. つまり、このメソッドは
verifier(3,4).then(...)
が返したプロミスを拒否し、その結果
verifier(3,4).then(...).catch(...)
ハンドラが jobQueue に挿入されます。
次に、イベントループに戻り、jobQueueから引き出す次のジョブは
verifier(5, 4)
ジョブです。 これは解決済みのプロミスと解決ハンドラを持っているので、そのハンドラを呼び出します。 これによって
response (5,4):
の出力が表示されます。
そして、イベントループに戻り、jobQueueから引き出す次のジョブは
verifier(3,4).then(...).catch(...)
ジョブが実行され、これによって
error (3,4)
の出力が表示されます。
が表示されるからです。
.catch()
の方がプロミスレベルとしては深いからです。
.then()
の方が1つ深いので、このような問題が発生します。 そして、プロミスチェーンは同期ではなく、FIFOオーダーでジョブキューを経由して1つのレベルから次のレベルへトラバースされるためです。
このレベルのスケジューリング詳細に依存することについての一般的な推奨事項
参考までに、一般的に、私はこのレベルの詳細なタイミングの知識に依存しないコードを書くようにしています。 理解することは興味深く、時には便利ですが、コードへの単純な一見無害な変更が相対的なタイミングの変更につながる可能性があるため、壊れやすいコードです。 ですから、このように2つのチェーン間のタイミングが重要な場合、私はこのレベルの詳細な理解に頼るのではなく、自分の望むタイミングを強制的に作り出すような方法でコードを書きたいと思います。
関連
-
[解決済み] JavaScriptで "use strict "は何をするのか、その根拠は?
-
[解決済み] JavaScriptで文字列が部分文字列を含むかどうかを確認する方法は?
-
[解決済み] あるJavaScriptファイルを他のJavaScriptファイルにインクルードするにはどうすればよいですか?
-
[解決済み] JavaScriptでメールアドレスを検証するのに最適な方法は何ですか?
-
[解決済み] JavaScriptでオブジェクトをディープクローンする最も効率的な方法は何ですか?
-
[解決済み】JavaScriptの比較では、どちらの等号演算子(== vs ===)を使うべきですか?
-
[解決済み】JavaScriptで文字列の出現箇所をすべて置換する方法
-
[解決済み】オブジェクトからプロパティを削除する(JavaScript)
-
[解決済み] javascriptで2つの数値を連結する方法は?
-
[解決済み] Javascriptで動的に命名されたメソッドを呼び出すにはどうすればよいですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] <Enter>でjQuery UIダイアログを送信する
-
[解決済み] Google maps API V3 - 同一地点に複数のマーカーを設置する。
-
[解決済み] javascript の関数から `undefined` と `null` のどちらを返すのが良いのでしょうか?
-
[解決済み] TypeScriptのdeclare classとinterfaceの違いとは?
-
[解決済み] Reactコンポーネントでthis.setStateを複数回使用するとどうなりますか?
-
[解決済み] JavaScriptで文字列を数値に変換する最速の方法は何ですか?
-
[解決済み] サブドメインにまたがってlocalStorageを使用する
-
[解決済み] AngularJS - ngRepeatフィルタリングされた結果の参照を取得する方法
-
[解決済み] javascriptでオプションのパラメータを扱う
-
[解決済み] 変異を伴わないオブジェクトからの値の削除