JS の Promise についての深い理解

yulia
リリース: 2018-09-10 14:52:32
オリジナル
1407 人が閲覧しました

Promise を使用する場合、最も簡単な理解と使用法は、上記のコードのようにパラメーターとして解決する非同期結果を提供し、カスタム関数を結果処理関数として then メソッドに渡すことです。しかし、2 つのパラメーターの解決と拒否とは正確には何でしょうか?舞台裏では、その基本的な仕組みとはどのようなものなのでしょうか?規範的な観点から予備的に見てみましょう。

new Promise((resolve, reject) => setTimeout(resolve, 1000, 'foo'))  
 .then(console.log)  
 // foo (1s后)
ログイン後にコピー

TL;DR
1. Promise の動作メカニズムはコールバックのメカニズムと似ており、どちらも内部抽象操作 Job を使用して非同期を実装します
2. Promise コンストラクターの解決/拒否関数は内部的に作成されます。渡されたパラメータは解析される結果であり、Promise に格納されているユーザーから渡された処理関数とともにジョブ キューに挿入されます。渡されるパラメーターは Promise にすることもでき、これは Promise.all/race で内部的に使用されます。
3. Promise.prototype.then は、現在の Promise ステータスに基づいて、Promise に格納されている結果をすぐに取り出し、パラメータ内の処理関数とともにジョブ キューに直接挿入するか、最初にそれを結果処理関数としてpromiseを使用します。次に、暗黙的に Promise 構築関数を呼び出して新しい Promise を構築し、それを返します。
4. Promise.all は、最初に新しい Promise を作成し、次に空の結果配列と解決された Promise をカウントするカウンターを初期化して、反復値の Promise ごとに新しい Promise を作成し、その then を設定します。結果とカウンターを結果配列に追加することを約束します。カウンターが 0 に減少すると、最終結果が解決されます。
5. Promise.race も新しいメインの Promise を作成します。その後、主に Promise は 1 回しか解決できないという制限に基づいて、反復値ごとに別の Promise が作成され、最初に解決されたものが返されます。メインプロミスの結果が最初に解決されます。

新しい Promise(executor)

まず、Promise コンストラクターから始めましょう。これは、グローバル オブジェクトの Promise 属性の値です。これが、同様にブラウザ環境で直接呼び出すことができる理由です。文字列、これらの配列コンストラクターは同じです。

新しい Promise(executor) の最初のステップは、他のコンストラクターと同様に、Promise のプロトタイプに従って新しいオブジェクトを構築し、いくつかの内部スロット [[PromiseState]]、[[PromiseResult]]、[[ PromiseFullfillReactions]]、[ [PromiseRejectReactions]]、[[PromiseIsHandled]] は、名前から大まかに推測できる機能を記録するために使用されます。ここで、[[PromiseResult]]以外の初期値は「pending」、空リスト、空リスト、falseです。

次のステップでは、ES は、この Promise オブジェクトに基づいて、Promise を解決するために使用される Resolve 関数と、Promise を拒否するために使用される Reject 関数を生成します。次に、resolve 関数と拒否関数をパラメータとしてエグゼキュータを呼び出します。このプロセス中にエラーが発生した場合は、Promise を直接拒否します。最後に約束を返す。

では、決意とは何で、拒否とは何でしょうか? Promise の状態、つまり [[PromiseState]] には、pending、fullfilled、rejected の 3 つの値があることがわかっています。reject 関数を使用して、Promise を拒否し、その状態を保留中から拒否済みに変更します。ただし、resolve 関数は、Promise をフルフィルして Promise のステータスを保留中からフルフィルドに変更することも、Promise を拒否するために使用することもできます。

それでは、resolve関数とreject関数は正確に何をするのでしょうか?

まず、reject 関数を見てみましょう。まず、生成時に [[Promise]] スロットと [[AlreadyResolved]] スロットを初期化します。つまり、それを Promise に関連付けます。実行中に、パラメータreasonが渡され、[[AlreadyResolved]]がfalseの場合、つまり未解決でステータスが保留中の場合にのみ、戻り値のRejectPromiseが呼び出され、promiseパラメータとreasonパラメータがPromise を拒否するために渡されます。それ以外の場合は、未定義を返します。
RejectPromise(promise,reason) は、[[PromiseState]] を保留中から拒否に変更するだけでなく、Promise 結果 [[PromiseResult]] の値をreason に設定し、Promise の [[PromiseRejectReactions]] を取り出します。 (読者は後でこの内部スロットにレコードを保存する操作があることを理解していると思います)、TriggerPromiseReactions を使用してこれらのレコードを後続の処理のために呼び出し、拒否の理由を渡します。同様に、resolve 関数で使用される FullfillPromise(promise, value) オペレーションは、Promise ステータスを履行済みに変更し、[[PromiseFullfillReactions]] の値を抽出し、TriggerPromiseReactions を呼び出し、履行された結果の値を渡します。

TriggerPromiseReactions(reactions, argument) は EnqueueJob("PromiseJobs", PromiseReactionJob, <>) を呼び出します。これについては後で詳しく説明します。

resolve 関数を見てみましょう。reject 関数と同様に、生成されると Promise に関連付けられます。実行時に渡すパラメーターは解像度と呼ばれます。 Promise が解決された場合は、unknown が返されます。その後の状況は比較的複雑です。

1. ユーザーがパラメータ解決としてpromise自体をresolve関数に渡すと、TypeErrorが作成されてスローされ、rejectPromiseが呼び出されます。
2. 解決のタイプが Object でない場合は、FulfillPromise(promise,solution) を呼び出します。
3. 残りのケースは、解決策が自分以外の then とのオブジェクト (Promise) である場合です。
解決が then のないオブジェクトの場合は、単に RejectPromise を実行します。
then 属性があるが呼び出せない場合は、FulfillPromise、.
then 属性があり、呼び出すことができる場合は、EnqueueJob("PromiseJobs", PromiseResolveThenableJob, <>) だけです。
EnqueueJob について説明する前に、まず Job とは何かを見てみましょう。簡単に言うと、「他の ES が実行されていないときに、対応する独自の ES を初期化して実行する」というコールバックの内部実装メカニズムのようなものです。実行する FIFO ジョブ キューがあり、現在の実行環境で実行コンテキストと実行コンテキスト スタックが空の場合、最初のジョブ キューが実行されます。

ES では、実装内に少なくとも 2 つのジョブ キュー (ScriptJobs と PromiseJobs) が存在する必要があると規定しています。 EnqueueJob("PromiseJobs", ...) を呼び出すと、完了するジョブとそのパラメータが PromiseJobs キューに挿入されます。ご覧のとおり、Promise

1 には 2 つのタイプのジョブがあります。PromiseReactionJob(reaction, argument)
reaction には 3 つの内部スロット [[Capability]]、[[Type]]、および [[Handler]] があり、それぞれを表します。 [[関連付け Promise と関連する解決関数と拒否関数]]、[[カテゴリ]]、[[ハンドラー]]。ユーザーがハンドラ(未定義)を指定しない場合、カテゴリが履行か拒否かに応じて引数が結果として使用されます。ハンドラーが指定されている場合、それは引数のさらなる処理に使用されます。最後に、resolve 関数と拒否関数を使用して、この結果に基づいて処理し、返します。
2. PromiseResolveThenableJob(promiseToResolve, thenable, then)
promiseToResolveに関連付けられたresolve関数とreject関数を作成します。 then を呼び出し関数として使用し、 thenable を this として使用し、呼び出して返すパラメーターとして解決関数と拒否関数を使用します。

Promise.prototype.then(onfulfilled, onrejected)

まず最初に、新しい Promise と、関連する解決関数と拒否関数を含む PromiseCapability を作成します。 Promiseの生成は通常のPromiseコンストラクタと同じようにPromiseを構築することですが、コンストラクタに渡されるexecutorは内部で自動的に作成され、その機能はPromiseCapabilityにresolve/reject関数を記録することになります。 PromiseJobs で実行される最終操作である、promiseCapability と onfulfilled/onrejected に基づいて、それぞれ、fulfill と reject の 2 つの PromiseReaction を作成します。 現在の Promise (this) が保留状態の場合、これら 2 つのリアクションを Promise の [[PromiseFulfillReactions]] キューと [[PromiseRejectReactions]] キューにそれぞれ挿入します。ただし、この時点で Promise がすでに履行されているか拒否されている場合、値の結果が Promise の [[PromiseResult]] から取得され、履行結果/拒否理由としてジョブ キューに挿入されます。 、<>)、最後に prjomiseCapability に保存されている新しい Promise を返します。 Promise.prototype.catch(onrejected) は Promise.prototype.then(unknown, onrejected) です

Promise.resolve(x)

then のようにpromiseCapabilityを作成し、その中でresolve関数を直接呼び出して、解決する値関数を指定し、それをイテレータ ループと結合します。 1. 反復が完了し、カウンタが 0 の場合、promiseCapability の replace 関数を呼び出して結果の配列を解決します。 2. それ以外の場合、カウンタは次のようにインクリメントされます。 1、そして次の反復の値が取り出されて Promise.resolve に渡されて新しい Promise も構築され、内部で Promise.all Resolve Element Function が作成され、この新しい Promise に渡される が使用されて追加されます。 result を結果配列に格納し、カウンターを 1 つデクリメントします。

Promise.race(iterable)同様に、promiseCapability を作成して反復し、Promise.resolve を使用して新しい Promise を構築し、この新しい Promise の then メソッドを呼び出して、promiseCapability Combined のsolve/reject 関数に渡します。前述の約束は一度だけ解決されるため、実際に非常に人種的であることがわかります。

結論:これを見て、みなさんはプロミスについての理解がさらに深まったでしょうか。さらに一歩進んで、ES6 で新たに提案された async/await は、Generator と Promise のアイデアを実際に適用しています。興味があれば、さらに学習を続けることができます。

以上がJS の Promise についての深い理解の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート