26

次のコードがどのように実行されるのか理解できません。なぜ「1」は「b」の後にあるのに「h」は「3」の後にあるのですか? 順番は、a、b、1、2、h、3 の順でよいでしょうか? 一部の記事では、「イベント ループ キュー」と「ジョブ キュー」の違いが次の出力につながると述べています。しかし、どのように?ECMAScript 2015 - 8.4 Jobs and Job Queuesの仕様を読み、Promise'job がどのように機能するかを知りたがっていますが、さらに混乱します。誰かが私を助けることができますか?ありがとうございました!

var promise = new Promise(function(resolve, reject) {resolve(1)});
promise.then(function(resolve) {console.log(1)});
console.log('a');
promise.then(function(resolve) {console.log(2);});
setTimeout(function() {console.log('h')}, 0);
promise.then(function(resolve) {console.log(3)});
console.log('b');

// a
// b
// 1
// 2
// 3
// h

Promise が非同期であることは知っていますが、setTimeout(..) 非同期操作のコールバックは常に Promise の非同期操作の後になります。なんで?

4

6 に答える 6

21

「b」の後に「1」があるのはなぜですか?

promise 仕様により、すべての promise.then()ハンドラーは、JS の現在のスレッドが完了するまで実行された後に非同期で呼び出されます。したがって、現在の JS の一部として同期的に実行されるaとの両方がハンドラの前に実行されるため、常に と の後に実行されます。b.then()1ab

興味深い読み物: T asks, microtasks, queue and schedules and What is the order of execution in javascript promises and Writing a JavaScript framework - Execution timing, beyond setTimeout .


このスレッドには、良いアドバイスがいくつかありnextTickますsetImmediate

非連鎖イベントの正確な実行順序に依存することはお勧めしません。実行順序を制御したい場合 — 後で実行したいコールバックが前に実行したいコールバックに依存するようにコールバックを再配置するか、キューを実装します (フードの後ろで同じことを行います)。 )。

つまり、非同期イベントの特定のタイミングに依存している場合は、コード内でそれらを実際にチェーンする必要があるため、実装で指定されていないスケジューリングに依存するのではなく、コードを介して次々に発生する必要があります。

于 2016-11-30T04:56:57.847 に答える
13

HTML 用語では、同じドメインの 1 ページまたは一連のページのイベント ループに、複数のタスク キューを含めることができます。同じタスク ソースからのタスクは常に同じキューに入れられ、次に使用するタスク キューはブラウザによって選択されます。

タイマー コールバックを実行するタスクは、タイマー タスク ソースから取得され、同じキューに入れられます。このキュータスク キューを "A"としましょう。

ECMAscript 2015 (ES6) 仕様では、タスクが Promise リアクション コールバックを実行して、 "PromiseJobs"と呼ばれる独自のジョブ キューを形成する必要があります。ECMAscript と HTML の仕様は同じ言語を使用していないため、ECMA の「Promise ジョブ キュー」をブラウザの HTMLタスク キュー「B」と概念的に同一視しましょう。少なくとも、タイマーで使用されるものとは異なるキューです。

理論的には、ブラウザーはキュー A または B から実行するタスクを選択できますが、実際には promise タスク キューの優先度が高くなり、タイマー コールバックが実行される前に空になります。

これが、「h」が最後に記録される理由です。then満たされた Promise に対するPromise呼び出しは、Promise キューにジョブを配置し、タイマー コールバックよりも高い優先度で実行されます。promise キューは、実行後console.log(3)にのみ空になり、タイマー コールバックを実行できるようになります。


高度

ECMAScript ガーディアンは、HTML5 の用語やタスク キューの説明を仕様に使用しないことを選択しました。これは、ECMAScript が HTML ブラウザーだけでなく、より多くの環境で実行できるためです。

プロミス キューのネイティブ実装では、別の専用のプロミス タスク キューの代わりに「マイクロ タスク」キューを使用できます。マイクロ キューに入れられたジョブは、現在のスクリプト スレッドと以前にマイクロ キューに追加されたタスクが完了した後に実行されます。

promise を理解するために、マイクロタスクのキューイングの詳細は必要ありません。

Promise のネイティブ サポートがないブラウザー (IE のすべてのバージョンなど) の Promise ポリフィルは、タイマーを使用し、Promise 反応とタイマー コールバックの順序に関して、ネイティブ実装とまったく同じように動作しない場合があります。

于 2016-11-30T07:24:40.963 に答える
4

これは、JS を初めて使用する人にとって理解しやすいものでした。

これは @getify の本からのコピペです

例えるなら、イベント ループ キューは、遊園地の乗り物に似ています。乗り物が終わったら、列の最後尾に戻って再び乗り物に乗る必要があります。しかし、ジョブ キューは、乗車を終えた後、列に割り込んですぐに元に戻るようなものです。

イベント ループ キュー - promise 以外のすべての非同期コールバックの場合、h

ジョブ キュー - promise に関連するすべての非同期コールバック用。1、2、3

同期 - a、b

于 2018-01-28T17:28:43.037 に答える