オリジナルリンク:ポイントI
約束は、私は、インタビュアーとして、私の知る限りでは、ほとんどの企業は約束についていくつか質問をします、90%以上の約束確率を尋ねた質問で面接の高周波フロントエンドです。あなたが仕様PromiseA +によると、書き込み対応をすることができた場合は、ソースコード、そして私が思うに、約束のインタビューを関連する問題のために、より完璧な答えを与えることができます。
私のアドバイスは、私は7回よりも少ない数の約束に書き込むためのソースコードを持って、それがテストに合格するために数回変更されたとき、あなたは再び記述する必要がおそらく最初のパス、制御規範を達成するために複数回書き込みされます。
ソースコードの実装の約束
/ ** * 1.新しい約束、エグゼキュータ・アクチュエータを通過する必要がある、アクチュエータ直ちに実行される * 2エグゼキュータは、2つのパラメータ、すなわち、解決及び拒否受け入れ *プロミスのみ保留中から3に拒否し、またはまで保留中から成就 したら、確認した状態* 4.約束は変更されません 、メソッド、2つのパラメータは、すなわち、成功したコールバックの約束は、onFulfilledその後、受信されている* 5。約束を *との約束が失敗したコールバックonRejected * 6.コールの場合そして、約束が成功したとき、実行はonFulfilled、およびパラメータとして渡された値の約束。 *約束は、その後、onRejectedの実装、故障の原因とパラメータとして渡された約束を失敗した場合。 *ステータスが約束を保留している場合、待機状態が決定され、順次(パブリッシュ・サブスクライブ)に対応する機能実行された後、onFulfilledとonRejected機能を格納する必要 * 7その後のパラメータがonFulfilled onRejectedおよびデフォルトできる * 8約束ができ次に、複数回は、この方法はプロミスプロミス返し *は、結果が返され9.場合は、これは、引数としてなり、その後、次の成功コールバックに渡される(onFulfilled) 例外がスローされた後での場合* 10、例外が次の失敗したコールバック(onRejected)にパラメータとして渡されます そして、* 11の約束は、その後の約束を待つ必要がある場合はリターンが続いていますこれが成功すると約束し、約束を実行待ちます 、それが失敗した場合*その後、左の失敗、その後、次の成功に行く * / ; constの=「保留」迄 のconst =成就「成就」; constが= 'REJECTED 「拒否; 関数プロミス(エグゼキュータ){ LET自己=この; self.status = PENDING; self.onFulfilled = []; //成功したコールバック self.onRejected = []; //失敗したコールバック // + 2.1 PromiseAの 機能解決(値){ IF(self.status === PENDING){ self.status =成就; self.value =値。 self.onFulfilled.forEach(FN => FN()); // PromiseA + 2.2.6.1 } } 関数(理由)を拒否{ もし(self.status === PENDING){ self.status = REJECTED。 self.reason =理由。 self.onRejected.forEach(FN => FN()); // PromiseA + 2.2.6.2 } } {試みる キュータ(決意、リジェクト); }キャッチ(E){ (E)を拒絶します。 } } Promise.prototype.then =関数(onFulfilled、onRejected){ // PromiseA + 2.2.1 / 2.2.5 PromiseA + / PromiseA + 2.2.7.3 / PromiseA + 2.2.7.4 onFulfilled = typeof演算onFulfilled === '関数'?onFulfilled:値=>値。 onRejected = typeof演算onRejected === '関数'?onRejected:理由=> {スロー理由}。 自己を聞かせ=この; // PromiseA + 2.2.7 letのpromise2 =新しい約束((解決、拒否)=> { もし{(self.statusが満たさ===) // PromiseA + 2.2.2 // PromiseA + 2.2.4 ---のsetTimeout のsetTimeout(()=> { 試みる{ // PromiseA + 2.2.7.1 LET X = onFulfilled(self.value ); resolvePromise(promise2、X、解決、拒否); }キャッチ(E){ // PromiseA + 2.2.7.2は 、(E)リジェクト } })と、 }そうであれば(self.status === REJECTED){ // PromiseA + 2.2.3 のsetTimeout(()=> { {試みる X = onRejected(self.reason)ましょう。 resolvePromise(promise2、X、拒否、解決)。 }キャッチ(E){ (E)を拒絶します。 } })。 }他(self.status === PENDING){もし self.onFulfilled.push(()=> { {たsetTimeout(()=> {試みる X = onFulfilled(self.value)させ、 resolvePromise(promise2、X、解決;)リジェクト }キャッチ(E){ (E)リジェクト; } })を、 })。 self.onRejected.push(()=> { たsetTimeout(()=> { 試す{ X = onRejected(self.reason)ましょう。 resolvePromise(promise2、X、拒否、解決)。 }キャッチ(E){ (E)を拒絶します。 } })。 }); } })。 promise2を返します。 } 関数resolvePromise(promise2、X、解決、リジェクト){ 自己せ=この; // PromiseA + 2.3.1 (promise2は=== X){場合 (新しいはTypeError( '連鎖サイクルを'))リジェクト。 } IF(X && typeof演算X === 'オブジェクト' || typeof演算X === '関数'){ 使用しましょう。//PromiseA+2.3.3.3.3只能调用一次 {試みる 次いで= x.thenましょう。 IF(typeof演算次いで=== '関数'){ //PromiseA+2.3.3 then.call(X(Y)=> { //PromiseA+2.3.3.1 場合(使用)リターン; = trueを使用; resolvePromise( promise2、Y、解決、拒否); }、(R)=> { //PromiseA+2.3.3.2 場合(使用)リターン; = trueを使用する; (r)を拒否; }); }他{ //PromiseA+2.3.3.4 場合(使用)リターン; =真の使用; 解決(X)。 } }キャッチ(E){ // PromiseA + 2.3.3.2 (使用)戻った場合。 =真の使用; (e)を拒絶します。 } }他{ // PromiseA + 2.3.3.4 解決(X)。 } } module.exportsは=約束。
特別なテストスクリプトは、テストコードのコンプライアンスPromiseA +仕様を記述することができています。
まず、達成するためのコードの約束は、次のコードを追加します。
Promise.defer = Promise.deferred =関数(){ せDFD = {}; dfd.promise =新しいプロミス((決意、リジェクト)=> { dfd.resolve =決意; dfd.reject =拒否; }); DFDを返します。 }
インストールのテストスクリプト:
NPM -g約束-アプラステストをインストール
もしソースファイルの名前promise.jsの現在の約束
そして、対応するディレクトリで次のコマンドを実行します。
約束-アプラス・テストのpromise.js
約束-アプラス・テスト872のテストケースの合計インチ 上記のコード、完全にすべてのユースケースによって。
上記の簡単な説明を達成するためにコードのビットを行うには(いくつかの他のコンテンツのコメントは非常に明確に書かれています):
-
onFulfilledまたは実行コンテキストスタックは唯一のプラットフォームのコードを含むまでonRejectedを呼び出してはなりません。仕様は言ったので呼び出しonFulfilledとonFulfilledは、setTimeoutメソッドを配置する必要があります。setTimeoutを使用のみシミュレートされた非同期、ネイティブの約束が達成されていません。
-
仕様が明確に述べられてもいるので、このフラグをusedd必要とする理由の機能resolvePromise年代:resolvePromiseとrejectPromiseの両方が呼び出されている場合、または同じ引数に複数の呼び出しが行われ、最初の呼び出しが優先され、それ以上の呼び出しは無視されます私たちは、ことを確認して、このフラグを一度だけ行う必要があります。
-
self.onRejectedとコールバックとコールバックの失敗は成功を格納し、保留中の状態変化、対応するコールバックの順序を指定する必要が、その後の時間から仕様2.6有望に従ってself.onFulfilled。
PromiseA +仕様(翻訳版)
PS:ここでは参考のために、仕様の私の翻訳です
用語
- そして、約束の方法は、動作はこの仕様に準拠した関数やオブジェクトは、存在しています
- thenable、オブジェクトまたは関数の方法があります
- 値が値であるとき、未定義/ thenableまたは約束を含む状態約束の成功、
- 例外は利用価値がスローされた例外をスローで
- その理由は、値であるとき、失敗した状態の約束
請求
2.1約束州
約束は、3つの状態でなければなりません、保留中の充足または拒否されます
2.1.1もし保留状態に約束
2.1.1.1 可以变成 fulfilled 或者是 rejected
約束は果たさ状態にした場合2.1.2
2.1.2.1 不会变成其它状态
2.1.2.2 必须有一个value值
状態の約束が拒否された場合は2.1.3
2.1.3.1 不会变成其它状态
2.1.3.2 必须有一个promise被reject的reason
その一般化は、次のとおりです。状態から満たさ約束だけ保留となり、または保留からrejected.promise成功になることができ、成功したvalue.promiseが失敗し、失敗の理由があります
次いで、2.2方法
約束は、最終的な結果にアクセスする方法を提供する必要があります
次いで、この方法は、約束の2つのパラメータを受け取り、
promise.then(onFulfilled, onRejected)
2.2.1 onFulfilledパラメータはオプションであり、onRejected
2.2.1.1 onFulfilled 必须是函数类型
2.2.1.2 onRejected 必须是函数类型
2.2.2 onFulfilledはの関数である場合:
2.2.2.1 必须在promise变成 fulfilled 时,调用 onFulfilled,参数是promise的value
2.2.2.2 在promise的状态不是 fulfilled 之前,不能调用
2.2.2.3 onFulfilled 只能被调用一次
2.2.3もしonRejectedはの関数であります:
2.2.3.1 必须在promise变成 rejected 时,调用 onRejected,参数是promise的reason
2.2.3.2 在promise的状态不是 rejected 之前,不能调用
2.2.3.3 onRejected 只能被调用一次
2.2.4 onFulfilledとonRejectedは、マイクロタスクでなければなりません
2.2.5 onFulfilledとonRejectedが関数として呼び出さなければなりません
2.2.6この方法は複数回呼び出すことができます
2.2.6.1 如果promise变成了 fulfilled态,所有的onFulfilled回调都需要按照then的顺序执行
2.2.6.2 如果promise变成了 rejected态,所有的onRejected回调都需要按照then的顺序执行
2.2.7は、約束を返す必要があります
promise2 = promise1.then(onFulfilled、onRejected)。
2.2.7.1 onFulfilled 或 onRejected 执行的结果为x,调用 resolvePromise
2.2.7.2 如果 onFulfilled 或者 onRejected 执行时抛出异常e,promise2需要被reject
2.2.7.3 如果 onFulfilled 不是一个函数,promise2 以promise1的值fulfilled
2.2.7.4 如果 onRejected 不是一个函数,promise2 以promise1的reason rejected
2.3 resolvePromise
resolvePromise(promise2、X、解決、拒否)
2.3.1 promise2とxが等しい場合、TypeError例外で約束を拒否
2.3.2 xがpromsieがある場合
2.3.2.1 如果x是pending态,那么promise必须要在pending,直到 x 变成 fulfilled or rejected.
2.3.2.2 如果 x 被 fulfilled, fulfill promise with the same value.
2.3.2.3 如果 x 被 rejected, reject promise with the same reason.
2.3.3もしxがオブジェクトまたは関数であります
2.3.3.1 let then = x.then.
2.3.3.2 如果 x.then 这步出错,那么 reject promise with e as the reason..
2.3.3.3 如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromise)
2.3.3.3.1 resolvePromiseFn 的 入参是 y, 执行 resolvePromise(promise2, y, resolve, reject);
2.3.3.3.2 rejectPromise 的 入参是 r, reject promise with r.
2.3.3.3.3 如果 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。
2.3.3.3.4 如果调用then抛出异常e
2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略
2.3.3.3.4.3 否则,reject promise with e as the reason
2.3.3.4 如果 then 不是一个function. fulfill promise with x.
2.3.4 xはオブジェクトまたは関数でない場合は、Xとの約束を果たします。
約束の他の方法
上記約束PromiseA +ソース仕様が満たされているが、天然の約束はまたなど、他の多数の方法を提供するが:
- Promise.resolve()
- Promise.reject()
- Promise.prototype.catch()
- Promise.prototype.finally()
- Promise.all()
- Promise.race()
それぞれの方法の実現に関する具体的な次のとおりです。
Promise.resolve
Promise.resolve(値)が所定値解像度の後にオブジェクトを送信することを約束を返します。
- 値がthenableオブジェクトである場合は、その最終状態を使用して、このthenableのオブジェクトを「追跡」になる約束を返します
- 入ってくる値自体が約束オブジェクトである場合には、Promise.resolveは、このオブジェクトを返すように変更しない約束を任意の変更を行うことはありません。
- それ以外の場合は、目標値の約束の成功状態に直接戻ります。
Promise.resolve =関数(パラメータ){ IF(プロミスのinstanceof PARAM){ 戻りPARAM。 } 新しい約束を返す((決意、リジェクト)=> { IF(PARAM && param.then && typeof演算param.then === '関数'){ たsetTimeout(()=> { param.then(決意、リジェクト); } ); }他{ 解決(PARAM); } })。 }
thenableオブジェクトの加算を実行するためのSetTimeout理由が実行ネイティブプロミスオブジェクトの結果に基づいて推定される、ネイティブ実行結果に次のテストコード:2040030;同じ逐次実行順序、のsetTimeoutの遅延が増加します。
テストコード:
せP = Promise.resolve(20)。 p.then((データ)=> { にconsole.log(データ); }); ({P2 = Promise.resolveましょう :関数(解決、拒否){次に 、解決(30) } })。 p2.then((データ)=> { にconsole.log(データ) })。 せP3 = Promise.resolve(新しいプロミス((解決、リジェクト)=> { 解決(400) }))。 p3.then((データ)=> { にconsole.log(データ) })。
Promise.reject
Promise.reject Promise.resolve方法と異なるパラメータPromise.reject()メソッドは、拒否理由が無傷であるように、後続のプロセスのパラメータとなります。
Promise.reject =関数(理由){ ((解決、リジェクト)=> {新たな約束を返す (理由)を拒否; }); }
Promise.prototype.catch
Promise.prototype.catchは、エラーが発生したときにコールバックを指定するために使用し、特殊な方法では、キャッチした後、.thenを続けることができます
Promise.prototype.catch =関数(onRejected){ (ヌル、onRejected)this.then返します。 }
Promise.prototype.finally
かかわらず、成功または失敗の、最終的にでてくる、そして最後に、あなたがして続けることができます。そして値は、その後の背面にそのまま渡されます。
Promise.prototype.finally =関数(コールバック){ this.then返す((値)=> { 戻りPromise.resolve(コールバック())を(()=> { 戻り値; }); }、(ERR) => { 戻りPromise.resolve(コールバック())を(()=>を{。 ERR投げます; }); }); }
Promise.all
Promise.all(約束)は約束のオブジェクトを返します。
- 渡された引数が空の反復可能であるならば約束のコールバックオブジェクトは(解決)を完了するだけで、この場合には、同期が行われ、その後、他が非同期である返されました。
- 入ってくるパラメータはどんな約束が含まれていない場合、それは非同期完了を返します。
- 約束はすべての約束を約束しているときに完了コールバックは、約束が含まれていません「完了」またはパラメータです。
- パラメータは約束が失敗した場合は、失敗の約束は、オブジェクトPromise.allを返します
- いずれの場合においても、約束の完了ステータスの結果は、配列を返すことであるPromise.all
Promise.allは=関数(約束){ ((解決、リジェクト)=> {新たなプロミス戻り インデックス= 0せ; ; [] =結果を聞かせて (promises.length === 0){場合 決意(結果); }そう{ 関数processValue(I、データ){ [i]はデータを=生じ; (++インデックス=== promises.length){場合 決意(結果); } } ための式(I = 0を聞かせて、私はpromises.lengthを<; I ++ ){ //約束[i]が可能是普通值 Promise.resolve(約束[I])を((データ)=> { processValue(I、データ)。 }、(ERR)=> { (ERR)リジェクト。 返します。 }); } } })。 }
テストコード:
VAR promise1 =新しいプロミス((解決、リジェクト)=> { 解決(3); }) VaRのpromise2 = 42。 VaRのpromise3 =新しいプロミス(関数(決意、リジェクト){ たsetTimeout(決意、100、 'FOO'); }); 。Promise.all([promise1、promise2、promise3])を(関数(値){ にconsole.log(値); // [3、42、 'FOO'] }、(ERR)=> { にconsole.log( ERR) })。 VARのP = Promise.all([])。//は直ちに解決される VAR P2 = Promise.all([1337、 "HI"])。//非約束値は無視されますが、評価は、非同期で実行されます はconsole.log(p)を。 console.log(P2) のsetTimeout(関数(){ にconsole.log( 'スタックが現在空です')。 にconsole.log(P2); });
Promise.race
Promise.race関数はプロミスを返し、最初のパスが完了したと同様にそれを完了することを約束。また、故障(拒否)することができ、(解決さ)を完成させることができる、最初の二つの完了に依存する方法です。
あなたはパラメータの配列が空で渡すと、返された約束は永遠に待機します。
反復は値を1以上の非コミットメントが含まれている場合、および/またはコミットメントが解決されましたが/拒否、Promise.raceは、第1の反復発見のための値を解決します。
Promise.race =機能(約束){ 返す新しい約束((解決、拒否)=> { 場合(promises.length === 0){ リターン; }他{ (I = 0ましょうのために、私はpromises.lengthを<; I ++){ Promise.resolve(約束[I])を((データ)=> { 解決(データ) のリターン; }、(ERR)=>を{ リジェクト(ERR); リターン; }); } } }) ; }
テストコード:
Promise.race([ 新しいプロミス((決意、リジェクト)=> {たsetTimeout(()=> {解決(100)}、1000)})、 未定義の、 新しいプロミス((解決リジェクト)=> {たsetTimeout(( )=> {拒否(100)}、100)}) ])を((データ)=> { にconsole.log( '成功'、データ); }、(ERR)=> { にconsole.log( 'ERR ;)ERR」 )}。 Promise.race([ 新しいプロミス((決意、リジェクト)=> {たsetTimeout(()=> {解決(100)}、1000)})、 新たな約束((決意、リジェクト)=> {たsetTimeout(()= > {解決(200)}、200)})、 新たな約束((決意、リジェクト)=> {たsetTimeout(()=> {拒否(100)}、100)}) ])。次に、((データ)= > { にconsole.log(データ); }、(ERR)=> });