您好,登錄后才能下訂單哦!
本文首發于 vivo互聯網技術 微信公眾號
鏈接: https://mp.weixin.qq.com/s/Lp_5BXdpm7G29Z7zT_S-bQ
作者:Morrain
了用法,原生提供了Promise對象。更多關于 Promise 的介紹請參考阮一峰老師的 ES6入門 之 Promise 對象。
很多同學在學習 Promise 時,知其然卻不知其所以然,對其中的用法理解不了。 本系列文章由淺入深逐步實現 Promise,并結合流程圖、實例以及動畫進行演示,達到深刻理解 Promise 用法的目的。
本系列文章有如下幾個章節組成:
圖解 Promise 實現原理(一)—— 基礎實現
圖解 Promise 實現原理(二)—— Promise 鏈式調用
圖解 Promise 實現原理(三)—— Promise 原型方法實現
圖解 Promise 實現原理(四)—— Promise 靜態方法實現
上一節中,實現了 Promise 的原型方法。包括增加異常狀態,catch以及 finally。截至目前,Promise 的實現如下:
class Promise { callbacks = []; state = 'pending';//增加狀態 value = null;//保存結果 constructor(fn) { fn(this._resolve.bind(this), this._reject.bind(this)); } then(onFulfilled, onRejected) { return new Promise((resolve, reject) => { this._handle({ onFulfilled: onFulfilled || null, onRejected: onRejected || null, resolve: resolve, reject: reject }); }); } catch(onError) { return this.then(null, onError); } finally(onDone) { if (typeof onDone !== 'function') return this.then(); let Promise = this.constructor; return this.then( value => Promise.resolve(onDone()).then(() => value), reason => Promise.resolve(onDone()).then(() => { throw reason }) ); } _handle(callback) { if (this.state === 'pending') { this.callbacks.push(callback); return; } let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected; if (!cb) {//如果then中沒有傳遞任何東西 cb = this.state === 'fulfilled' ? callback.resolve : callback.reject; cb(this.value); return; } let ret; try { ret = cb(this.value); cb = this.state === 'fulfilled' ? callback.resolve : callback.reject; } catch (error) { ret = error; cb = callback.reject } finally { cb(ret); } } _resolve(value) { if (value && (typeof value === 'object' || typeof value === 'function')) { var then = value.then; if (typeof then === 'function') { then.call(value, this._resolve.bind(this), this._reject.bind(this)); return; } } this.state = 'fulfilled';//改變狀態 this.value = value;//保存結果 this.callbacks.forEach(callback => this._handle(callback)); } _reject(error) { this.state = 'rejected'; this.value = error; this.callbacks.forEach(callback => this._handle(callback)); } }
接下來再介紹一下 Promise 中靜態方法的實現,譬如 Promise.resolve、Promise.reject、Promise.all 和 Promise.race。其它靜態方法的實現也是類似的。
除了前文中提到的 Promise實例的原型方法外,Promise 還提供了 Promise.resolve 和Promise.reject 方法。用于將非 Promise 實例包裝為 Promise 實例。例如:
Promise.resolve('foo') // 等價于 new Promise(resolve => resolve('foo'))
Promise.resolve 的參數不同對應的處理也不同,如果 Promise.resolve 的參數是一個 Promise的實例,那么 Promise.resolve 將不做任何改動,直接返回這個 Promise 實例,如果是一個基本數據類型,譬如上例中的字符串,Promise.resolve 就會新建一個 Promise 實例返回。這樣當我們不清楚拿到的對象到底是不是 Promise 實例時,為了保證統一的行為,Promise.resolve 就變得很有用了。看一個例子:
const Id2NameMap = {}; const getNameById = function (id) { if (Id2NameMap[id]) return Id2NameMap[id]; return new Promise(resolve => { mockGetNameById(id, function (name) { Id2NameMap[id] = name; resolve(name); }) }); } getNameById(id).then(name => { console.log(name); });
上面的場景我們會經常碰到,為了減少請求,經常會緩存數據,我們獲取到 id 對應的名字后,存到 Id2NameMap 對象里,下次再通過 id 去請求 id 對應的 name 時先看 Id2NameMap里有沒有,如果有就直接返回對應的 name,如果沒有就發起異步請求,獲取到后放到 Id2NameMap 中去。
其實上面的代碼是有問題的,如果命中 Id2NameMap 里的值,getNameById 返回的結果就是 name,而不是 Promise 實例。此時 getNameById(id).then 會報錯。在我們不清楚返回的是否是 Promise 實例的情況下,就可以使用 Promise.resolve 進行包裝:
Promise.resolve(getNameById(id)).then(name => { console.log(name); });
這樣一來,不管 getNameById(id) 返回的是什么,邏輯都沒有問題。看下面的Demo:
demo-Promise.resolve 的源碼
在實現 Promise.resolve 之前,我們先看下它的參數分為哪些情況:
(1)參數是一個 Promise 實例
如果參數是 Promise 實例,那么 Promise.resolve 將不做任何修改、原封不動地返回這個實例。
(2)參數是一個 thenable 對象
thenable 對象指的是具有 then 方法的對象,比如下面這個對象。
let thenable = { then: function(onFulfilled) { onFulfilled(42); } };[object Object]
Promise.resolve 方法會將這個對象轉為 Promise 對象,然后就立即執行 thenable 對象的 then方法。
let thenable = { then: function(onFulfilled) { onFulfilled(42); } }; let p1 = Promise.resolve(thenable); p1.then(function(value) { console.log(value); // 42 });
上面代碼中,thenable對象的then方法執行后,對象p1的狀態就變為resolved,從而立即執行最后那個then方法指定的回調函數,輸出 42。
(3)參數不是具有 then 方法的對象,或根本就不是對象
如果參數是一個原始值,或者是一個不具有then方法的對象,則 Promise.resolve 方法返回一個新的 Promise 對象,狀態為 resolved。
(4)不帶任務參數
Promise.resolve 方法允許調用時不帶參數,直接返回一個 resolved 狀態的 Promise 對象。
static resolve(value) { if (value && value instanceof Promise) { return value; } else if (value && typeof value === 'object' && typeof value.then === 'function') { let then = value.then; return new Promise(resolve => { then(resolve); }); } else if (value) { return new Promise(resolve => resolve(value)); } else { return new Promise(resolve => resolve()); } }
Promise.reject 與 Promise.resolve 類似,區別在于 Promise.reject 始終返回一個狀態的 rejected 的 Promise 實例,而 Promise.resolve 的參數如果是一個 Promise 實例的話,返回的是參數對應的 Promise 實例,所以狀態不一定。
Promise.reject 的實現源碼
Promise.all 接收一個 Promise 實例的數組,在所有這些 Promise 的實例都 fulfilled 后,按照 Promise 實例的順序返回相應結果的數組。
const p1 = new Promise((resolve, reject) => { setTimeout(() => resolve('p1'), 1000) }) const p2 = new Promise((resolve, reject) => { setTimeout(() => resolve('p2'), 5000) }) Promise.all([p1, p2]).then(rets => { console.log(rets) // ['p1','p2'] })
Promise.all 的實現如下:
static all(promises) { return new Promise((resolve, reject) => { let fulfilledCount = 0 const itemNum = promises.length const rets = Array.from({ length: itemNum }) promises.forEach((promise, index) => { Promise.resolve(promise).then(result => { fulfilledCount++; rets[index] = result; if (fulfilledCount === itemNum) { resolve(rets); } }, reason => reject(reason)); }) }) }
Promise.all 的實現源碼
Promise.race 也接收一個 Promise 實例的數組,與 Promise.all不同的是,所以返回的結果是這些 Promise 實例中最先 fulfilled 的。
const p1 = new Promise((resolve, reject) => { setTimeout(() => resolve('p1'), 1000) }) const p2 = new Promise((resolve, reject) => { setTimeout(() => resolve('p2'), 5000) }) Promise.race([p1, p2]).then(ret => { console.log(ret) // 'p1' })
Promise.race 的實現如下:
static race(promises) { return new Promise(function (resolve, reject) { for (let i = 0; i < promises.length; i++) { Promise.resolve(promises[i]).then(function (value) { return resolve(value) }, function (reason) { return reject(reason) }) } }) }
Promise.race 的實現源碼
剛開始看 Promise 源碼的時候總不能很好的理解 then 和 resolve 函數的運行機理,但是如果你靜下心來,反過來根據執行 Promise 時的邏輯來推演,就不難理解了。這里一定要注意的點是:Promise 里面的 then 函數僅僅是注冊了后續需要執行的代碼,真正的執行是在 resolve 方法里面執行的,理清了這層,再來分析源碼會省力的多。
現在回顧下 Promise 的實現過程,其主要使用了設計模式中的觀察者模式:
通過 Promise.prototype.then 和 Promise.prototype.catch 方法將觀察者方法注冊到被觀察者 Promise 對象中,同時返回一個新的 Promise 對象,以便可以鏈式調用。
被觀察者管理內部 pending、fulfilled 和 rejected 的狀態轉變,同時通過構造函數中傳遞的 resolve 和 reject 方法以主動觸發狀態轉變和通知觀察者。
本系列圖文講解的是 Promise 的思想,實現的內容并不能完全滿足 Promise/A+ 規范的所有要求。
【翻譯】Promises/A+規范
深入 Promise(一)——Promise 實現詳解
30分鐘,讓你徹底明白Promise原理
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。