完全な Promise を実装する方法
Promiseを使ったことがあるのですが、いつもよくわからないと感じました。たくさんの記事を読んだのですが、それでもPromiseの実装原理が理解できませんでした。その後、記事を読んでコードをデバッグしました。徐々に感覚をつかんできたのですが、以下は自分の理解に従ってPromiseを実装するだけです。
コードを完全に理解するには、この とクロージャ の意味を理解する必要があります。
Promise とは
簡単に言えば、Promise は主に非同期コールバックの問題を解決することを目的としています。 Promise を使用して非同期コールバックを処理すると、コード階層が明確になり、理解しやすく、保守しやすくなります。現在の主流の仕様は主に 約束/A+。 Promise の使い方がよくわからない場合は、私の記事「es6 勉強記 5 - promise」を参照して理解できたら、この記事を読んでください。
始める前に、Promise の役割を体験するために、Promise アプリケーションのシナリオを書いてみましょう。 Google と Firefox は現在、es6 Promise をサポートしています。 setTimeout を使用して非同期操作をシミュレートします。具体的なコードは次のとおりです。
function fn1(resolve, reject) { setTimeout(function() { console.log('步骤一:执行'); resolve('1'); },500); } function fn2(resolve, reject) { setTimeout(function() { console.log('步骤二:执行'); resolve('2'); },100); } new Promise(fn1).then(function(val){ console.log(val); return new Promise(fn2); }).then(function(val){ console.log(val); return 33; }).then(function(val){ console.log(val); });
最終的に、私たちが書いた Promise もこの機能を実現できます。
初期構築
簡単なpromsieを書いてみましょう。 Promiseの引数は関数fnで、内部で定義したresolveメソッドを引数としてfnに渡してfnを呼び出します。非同期操作が成功すると、resolveメソッドが呼び出され、そこに登録されているコールバック関数が実行されます。
function Promise(fn){ //需要一个成功时的回调 var callback; //一个实例的方法,用来注册异步事件 this.then = function(done){ callback = done; } function resolve(){ callback(); } fn(resolve); }
チェーンのサポートを追加します
以下にチェーンを追加します。成功したコールバック メソッドは、保存する前に配列に変換する必要があります。同時に、resolveメソッドにパラメータを追加し、未定義が出力されないようにします。
function Promise(fn) { var promise = this, value = null; promise._resolves = []; this.then = function (onFulfilled) { promise._resolves.push(onFulfilled); return this; }; function resolve(value) { promise._resolves.forEach(function (callback) { callback(value); }); } fn(resolve); }
promise = this なので、ある時点でこのポインタが突然変化することを心配する必要はありません。
then メソッドを呼び出し、promise._resolvesキューにコールバックを入れます。
非同期操作のときに、Promise オブジェクトを作成し、その fn を呼び出し、resolve メソッド を渡します。 fn が正常に実行されました。その後、resolve が呼び出されます。つまり、
<code><span style="color:#000000">promise._resolves</span>
队列中的回调;<span style="color:#000000">resolve 方法</span>
接收一个参数,即异步操作返回的结果,方便传值。then方法中的 return this 实现了链式调用。
但是,目前的 Promise 还存在一些问题,如果我传入的是一个不包含异步操作的函数,resolve就会先于 then 执行,也就是说 promise._resolves 是一个空数组。
为了解决这个问题,我们可以在 resolve 中添加 setTimeout,来将 resolve
promise._resolves チームの
</p>resolve メソッド<p>
は、非同期操作によって返された結果であるパラメーター
を受け取るため、値を渡します。
then メソッドの return this はチェーン呼び出しを実装します。
ただし、現在の Promise にはまだ問題がいくつかあります。非同期操作を含まない関数を渡すと、その前に replace が実行されます。つまり、
promise._resolves 配列が空になります。 。 🎜🎜 この問題を解決するには、resolve に setTimeout を追加して、resolve
内のコールバック実行ロジックを JS タスク キューの最後に配置します。 🎜🎜function resolve(value) { setTimeout(function() { promise._resolves.forEach(function (callback) { callback(value); }); },0); }
<p style="margin-bottom: 7px;">function Promise(fn) {<br/> var promise = this,<br/> value = null;<br/> promise._resolves = [];<br/> promise._status = 'PENDING';<br/><br/> this.then = function (onFulfilled) {<br/> if (promise._status === 'PENDING') {<br/> promise._resolves.push(onFulfilled);<br/> return this;<br/> }<br/> onFulfilled(value);<br/> return this;<br/> };<br/> function resolve(value) {<br/> setTimeout(function(){<br/> promise._status = "FULFILLED";<br/> promise._resolves.forEach(function (callback) {<br/> callback(value);<br/> })<br/> },0);<br/> }<br/><br/> fn(resolve);<br/>}<br/></p>
function resolve(value) { setTimeout(function(){ promise._status = "FULFILLED"; promise._resolves.forEach(function (callback) { value = callback(value); }) },0); }
this.then = function (onFulfilled) { return new Promise(function(resolve) { function handle(value) { var ret = isFunction(onFulfilled) && onFulfilled(value) || value; resolve(ret); } if (promise._status === 'PENDING') { promise._resolves.push(handle); } else if(promise._status === FULFILLED){ handle(value); } }) };
注意的是,new Promise() 中匿名函数中的 promise (promise._resolves中的 promise)指向的都是上一个 promise 对象, 而不是当前这个刚刚创建的。
首先我们返回的是新的一个promise对象,因为是同类型,所以链式仍然可以实现。
其次,我们添加了一个 handle 函数,handle 函数对上一个 promise 的 then 中回调进行了处理,并且调用了当前的 promise 中的 resolve 方法。
接着将 handle 函数添加到 上一个promise 的 promise._resolves 中,当异步操作成功后就会执行 handle 函数,这样就可以 执行 当前 promise 对象的回调方法。我们的目的就达到了。
有些人在这里可能会有点犯晕,有必要对执行过程分析一下,具体参看以下代码:
new Promise(fn1).then(fn2).then(fn3)})
fn1, fn2, fn3的函数具体可参看最前面的定义。
首先我们创建了一个 Promise 实例,这里叫做 promise1;接着会运行 fn1(resolve);
但是 fn1 中有一个 setTimeout 函数,于是就会先跳过这一部分,运行后面的第一个 then 方法;
then 返回一个新的对象 promise2, promise2 对象的 resolve方法和 then 方法的中回调函数 fn2 都被封装在 handle 中, 然后 handle 被添加到 promise1._resolves 数组中。
接着运行第二个 then 方法,同样返回一个新的对象 promise3, 包含 promise3 的 resolve 方法和回调函数 fn3 的 handle 方法被添加到 promise2._resolves 数组中。
到此两个 then 运行结束。 setTimeout 中的延迟时间一到,就会调用 promise1的 resolve方法。
resolve 方法的执行,会调用 promise1._resolves 数组中的回调,之前我们添加的 handle 方法就会被执行; 也就是 fn2 和 promsie2 的 resolve 方法,都被调用了。
以此类推,fn3 会和promise3 的 resolve 方法 一起执行,因为后面没有 then 方法了,promise3._resolves 数组是空的 。
至此所有回调执行结束
但这里还存在一个问题,就是我们的 then 里面函数不能对 Promise 对象进行处理。这里我们需要再次对 then 进行修改,使其能够处理 promise 对象。
this.then = function (onFulfilled) { return new Promise(function(resolve) { function handle(value) { var ret = typeof onFulfilled === 'function' && onFulfilled(value) || value; if( ret && typeof ret ['then'] == 'function'){ ret.then(function(value){ resolve(value); }); } else { resolve(ret); } } if (promise._status === 'PENDING') { promise._resolves.push(handle); } else if(promise._status === FULFILLED){ handle(value); } }) };
在 then 方法里面,我们对 ret 进行了判断,如果是一个 promise 对象,就会调用其 then 方法,形成一个嵌套,直到其不是promise对象为止。同时 在 then 方法中我们添加了调用 resolve 方法,这样链式得以维持。
失败处理
异步操作不可能都成功,在异步操作失败时,标记其状态为 rejected,并执行注册的失败回调。
有了之前处理 fulfilled 状态的经验,支持错误处理变得很容易。毫无疑问的是,在注册回调、处理状态变更上都要加入新的逻辑:
this.then = function (onFulfilled, onRejected) { return new Promise(function(resolve, reject) { function handle(value) { ....... } function errback(reason){ reason = isFunction(onRejected) && onRejected(reason) || reason; reject(reason); } if (promise._status === 'PENDING') { promise._resolves.push(handle); promise._rejects.push(errback); } else if(promise._status === 'FULFILLED'){ handle(value); } else if(promise._status === 'REJECTED') { errback(promise._reason); } }) }; function reject(value) { setTimeout(function(){ promise._status = "REJECTED"; promise._rejects.forEach(function (callback) { promise._reason = callback( value); }) },0); }
添加Promise.all方法
Promise.all 可以接收一个元素为 Promise 对象的数组作为参数,当这个数组里面所有的 Promise 对象都变为 resolve 时,该方法才会返回。
具体代码如下:
Promise.all = function(promises){ if (!Array.isArray(promises)) { throw new TypeError('You must pass an array to all.'); } // 返回一个promise 实例 return new Promise(function(resolve,reject){ var i = 0, result = [], len = promises.length, count = len; // 每一个 promise 执行成功后,就会调用一次 resolve 函数 function resolver(index) { return function(value) { resolveAll(index, value); }; } function rejecter(reason){ reject(reason); } function resolveAll(index,value){ // 存储每一个promise的参数 result[index] = value; // 等于0 表明所有的promise 都已经运行完成,执行resolve函数 if( --count == 0){ resolve(result) } } // 依次循环执行每个promise for (; i < len; i++) { // 若有一个失败,就执行rejecter函数 promises[i].then(resolver(i),rejecter); } }); }
Promise.all会返回一个 Promise 实例,该实例直到参数中的所有的 promise 都执行成功,才会执行成功回调,一个失败就会执行失败回调。
日常开发中经常会遇到这样的需求,在不同的接口请求数据然后拼合成自己所需的数据,通常这些接口之间没有关联(例如不需要前一个接口的数据作为后一个接口的参数),这个时候 Promise.all 方法就可以派上用场了。
添加Promise.race方法
该函数和 Promise.all 相类似,它同样接收一个数组,不同的是只要该数组中的任意一个 Promise 对象的状态发生变化(无论是 resolve 还是 reject)该方法都会返回。我们只需要对 Promise.all 方法稍加修改就可以了。
Promise.race = function(promises){ if (!Array.isArray(promises)) { throw new TypeError('You must pass an array to race.'); } return Promise(function(resolve,reject){ var i = 0, len = promises.length; function resolver(value) { resolve(value); } function rejecter(reason){ reject(reason); } for (; i < len; i++) { promises[i].then(resolver,rejecter); } }); }
代码中没有类似一个 resolveAll 的函数,因为我们不需要等待所有的 promise 对象状态都发生变化,只要一个就可以了。
添加其他API以及封装函数
到这里,Promise 的主要API都已经完成了,另外我们在添加一些比较常见的方法。也对一些可能出现的错误进行了处理,最后对其进行封装。
完整的代码如下:
(function(window,undefined){ // resolve 和 reject 最终都会调用该函数 var final = function(status,value){ var promise = this, fn, st; if(promise._status !== 'PENDING') return; // 所以的执行都是异步调用,保证then是先执行的 setTimeout(function(){ promise._status = status; st = promise._status === 'FULFILLED' queue = promise[st ? '_resolves' : '_rejects']; while(fn = queue.shift()) { value = fn.call(promise, value) || value; } promise[st ? '_value' : '_reason'] = value; promise['_resolves'] = promise['_rejects'] = undefined; }); } //参数是一个函数,内部提供两个函数作为该函数的参数,分别是resolve 和 reject var Promise = function(resolver){ if (!(typeof resolver === 'function' )) throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); //如果不是promise实例,就new一个 if(!(this instanceof Promise)) return new Promise(resolver); var promise = this; promise._value; promise._reason; promise._status = 'PENDING'; //存储状态 promise._resolves = []; promise._rejects = []; // var resolve = function(value) { //由於apply參數是數組 final.apply(promise,['FULFILLED'].concat([value])); } var reject = function(reason){ final.apply(promise,['REJECTED'].concat([reason])); } resolver(resolve,reject); } Promise.prototype.then = function(onFulfilled,onRejected){ var promise = this; // 每次返回一个promise,保证是可thenable的 return new Promise(function(resolve,reject){ function handle(value) { // 這一步很關鍵,只有這樣才可以將值傳遞給下一個resolve var ret = typeof onFulfilled === 'function' && onFulfilled(value) || value; //判断是不是promise 对象 if (ret && typeof ret ['then'] == 'function') { ret.then(function(value) { resolve(value); }, function(reason) { reject(reason); }); } else { resolve(ret); } } function errback(reason){ reason = typeof onRejected === 'function' && onRejected(reason) || reason; reject(reason); } if(promise._status === 'PENDING'){ promise._resolves.push(handle); promise._rejects.push(errback); }else if(promise._status === FULFILLED){ // 状态改变后的then操作,立刻执行 callback(promise._value); }else if(promise._status === REJECTED){ errback(promise._reason); } }); } Promise.prototype.catch = function(onRejected){ return this.then(undefined, onRejected) } Promise.prototype.delay = function(ms,value){ return this.then(function(ori){ return Promise.delay(ms,value || ori); }) } Promise.delay = function(ms,value){ return new Promise(function(resolve,reject){ setTimeout(function(){ resolve(value); console.log('1'); },ms); }) } Promise.resolve = function(arg){ return new Promise(function(resolve,reject){ resolve(arg) }) } Promise.reject = function(arg){ return Promise(function(resolve,reject){ reject(arg) }) } Promise.all = function(promises){ if (!Array.isArray(promises)) { throw new TypeError('You must pass an array to all.'); } return Promise(function(resolve,reject){ var i = 0, result = [], len = promises.length, count = len //这里与race中的函数相比,多了一层嵌套,要传入index function resolver(index) { return function(value) { resolveAll(index, value); }; } function rejecter(reason){ reject(reason); } function resolveAll(index,value){ result[index] = value; if( --count == 0){ resolve(result) } } for (; i < len; i++) { promises[i].then(resolver(i),rejecter); } }); } Promise.race = function(promises){ if (!Array.isArray(promises)) { throw new TypeError('You must pass an array to race.'); } return Promise(function(resolve,reject){ var i = 0, len = promises.length; function resolver(value) { resolve(value); } function rejecter(reason){ reject(reason); } for (; i < len; i++) { promises[i].then(resolver,rejecter); } }); } window.Promise = Promise; })(window);
代码写完了,总要写几个实例看看效果啊,具体看下面的测试代码:
var getData100 = function(){ return new Promise(function(resolve,reject){ setTimeout(function(){ resolve('100ms'); },1000); }); } var getData200 = function(){ return new Promise(function(resolve,reject){ setTimeout(function(){ resolve('200ms'); },2000); }); } var getData300 = function(){ return new Promise(function(resolve,reject){ setTimeout(function(){ reject('reject'); },3000); }); } getData100().then(function(data){ console.log(data); // 100ms return getData200(); }).then(function(data){ console.log(data); // 200ms return getData300(); }).then(function(data){ console.log(data); }, function(data){ console.log(data); // 'reject' }); Promise.all([getData100(), getData200()]).then(function(data){ console.log(data); // [ "100ms", "200ms" ] }); Promise.race([getData100(), getData200(), getData300()]).then(function(data){ console.log(data); // 100ms }); Promise.resolve('resolve').then(function(data){ console.log(data); //'resolve' }) Promise.reject('reject函数').then(function(data){ console.log(data); }, function(data){ console.log(data); //'reject函数' })
以上が完全な Promise を実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









WeChat は主流のチャット ツールの 1 つであり、WeChat を通じて新しい友人に出会ったり、古い友人に連絡したり、友人間の友情を維持したりすることができます。永遠に終わらない宴会など存在しないように、人間同士が仲良くなれば必ず意見の相違が生じます。ある人があなたの気分に極度に影響を与える場合、または仲良くなったときに意見が一致しないことがわかり、コミュニケーションが取れなくなった場合、WeChat の友人を削除する必要がある場合があります。 WeChatの友達を削除するにはどうすればよいですか? WeChat友達を削除する最初のステップ:WeChatメインインターフェースで[アドレス帳]をタップします; 2番目のステップ:削除したい友達をクリックして[詳細]を入力します; 3番目のステップ:上部の[...]をクリックします右隅; ステップ 4: 下の [削除] をクリックします; ステップ 5: ページのプロンプトを理解した後、[連絡先を削除] をクリックします;

トマト ノベルは非常に人気のある小説閲覧ソフトウェアです。トマト ノベルでは、新しい小説や漫画を読むことができます。どの小説も漫画もとても面白いです。小説を書きたい友達もたくさんいます。お小遣いを稼いで、小説の内容を編集することもできます。 「テキストに文章を書きたいです。それで、小説はどうやって書くのですか?友達は知らないので、一緒にこのサイトに行きましょう。小説の書き方の入門を少し見てみましょう。」 Tomato Novels を使用して小説を書く方法に関するチュートリアルを共有します。 1. まず、携帯電話で Tomato Free Novels アプリを開き、パーソナル センター - ライター センターをクリックします。 2. Tomato Writer Assistant ページに移動し、次の場所で [新しい本の作成] をクリックします。小説の終わり

Colorful マザーボードは中国国内市場で高い人気と市場シェアを誇っていますが、Colorful マザーボードのユーザーの中には、設定のために BIOS を入力する方法がまだ分からない人もいます。この状況に対応して、編集者はカラフルなマザーボード BIOS に入る 2 つの方法を特別に提供しました。ぜひ試してみてください。方法 1: U ディスク起動ショートカット キーを使用して、U ディスク インストール システムに直接入ります。ワンクリックで U ディスクを起動する Colorful マザーボードのショートカット キーは ESC または F11 です。まず、Black Shark インストール マスターを使用して、Black Shark インストール マスターを作成します。 Shark U ディスク起動ディスクを選択し、コンピュータの電源を入れます。起動画面が表示されたら、キーボードの ESC キーまたは F11 キーを押し続けて、起動項目を順次選択するウィンドウに入ります。「USB」の場所にカーソルを移動します。 」と表示され、その後

残念ながら、WeChat は広く使用されているソーシャル ソフトウェアであり、何らかの理由で特定の連絡先を誤って削除してしまうことがよくあります。ユーザーがこの問題を解決できるように、この記事では、削除された連絡先を簡単な方法で取得する方法を紹介します。 1. WeChat の連絡先削除メカニズムを理解します。これにより、削除された連絡先を取得できるようになります。WeChat の連絡先削除メカニズムでは、連絡先がアドレス帳から削除されますが、完全には削除されません。 2. WeChat の組み込みの「連絡先帳復元」機能を使用します。WeChat には、この機能を通じて以前に削除した連絡先をすばやく復元できる「連絡先帳復元」機能が用意されています。 3. WeChat 設定ページに入り、右下隅をクリックし、WeChat アプリケーション「Me」を開き、右上隅にある設定アイコンをクリックして設定ページに入ります。

携帯電話が人々の日常生活において重要なツールになるにつれて、フォント サイズの設定は重要なパーソナライゼーション要件になりました。さまざまなユーザーのニーズを満たすために、この記事では、簡単な操作で携帯電話の使用体験を向上させ、携帯電話のフォントサイズを調整する方法を紹介します。携帯電話のフォント サイズを調整する必要があるのはなぜですか - フォント サイズを調整すると、テキストがより鮮明で読みやすくなります - さまざまな年齢のユーザーの読書ニーズに適しています - フォント サイズを使用すると、視力の悪いユーザーにとって便利です携帯電話システムの設定機能 - システム設定インターフェイスに入る方法 - 設定インターフェイスで「表示」オプションを見つけて入力します。 - 「フォント サイズ」オプションを見つけて、サードパーティでフォント サイズを調整します。アプリケーション - フォント サイズの調整をサポートするアプリケーションをダウンロードしてインストールします - アプリケーションを開いて、関連する設定インターフェイスに入ります - 個人に応じて

Win11 管理者権限の取得方法のまとめ. Windows 11 オペレーティング システムでは、管理者権限は、ユーザーがシステム上でさまざまな操作を実行できるようにする非常に重要な権限の 1 つです。ソフトウェアのインストールやシステム設定の変更など、一部の操作を完了するために管理者権限の取得が必要になる場合があります。以下にWin11の管理者権限を取得する方法をまとめましたので、お役に立てれば幸いです。 1. ショートカット キーを使用する Windows 11 システムでは、ショートカット キーを使用してコマンド プロンプトをすばやく開くことができます。

テクノロジーの発展に伴い、モバイルゲームは人々の生活に欠かせないものになりました。かわいいドラゴンエッグの画像と面白い孵化過程で多くのプレイヤーの注目を集めており、その中でも注目を集めているゲームの一つがモバイル版ドラゴンエッグです。プレイヤーがゲーム内で自分のドラゴンをより適切に育成し成長させることができるように、この記事ではモバイル版でドラゴンの卵を孵化させる方法を紹介します。 1. 適切な種類のドラゴン エッグを選択する プレイヤーは、ゲーム内で提供されるさまざまな種類のドラゴン エッグの属性と能力に基づいて、自分に適したドラゴン エッグの種類を慎重に選択する必要があります。 2. 孵化機のレベルをアップグレードします。プレイヤーはタスクを完了し、小道具を収集することで孵化機のレベルを向上させる必要があります。孵化機のレベルは孵化速度と孵化成功率を決定します。 3. プレイヤーはゲームに参加する必要がある孵化に必要なリソースを収集します。

Oracleバージョンのクエリ方法を詳しく解説 Oracleは、世界で最も人気のあるリレーショナルデータベース管理システムの1つで、豊富な機能と強力なパフォーマンスを提供し、企業で広く使用されています。データベースの管理と開発のプロセスでは、Oracle データベースのバージョンを理解することが非常に重要です。この記事では、Oracle データベースのバージョン情報をクエリする方法と具体的なコード例を詳しく紹介します。単純な SQL ステートメントを実行して、Oracle データベース内の SQL ステートメントのデータベース バージョンをクエリします。
