今天,我帶着你們一步一步跟着規範實現一個本身的Promise
,你們能夠對照個人第二篇文章Promise介紹--規範篇或官方規範來一一學習。git
Promise
內部有三個固定的狀態,咱們在文件中提早定義。github
const PENDING = "pending" const FULFILLED = "fulfilled" const REJECTED = "rejected"
首先,是構造器constructor
。segmentfault
constructor(resolver){ this._status = PENDING; // 保存內部的狀態 this._result = undefined; // 保存promise對象fulfill或reject的最終結果 this._childArr = []; // 調用then方法建立的子promise對象 this._fulfillArr = []; // 調用then方法添加的onFulfilled方法 this._rejectArr = []; // 調用then方法添加的onRejected方法 if (resolver == noop) { return}; // then方法內部建立promise時間使用 // 若是resolver不是函數,則拋出TypeError錯誤 if (!isFunction(resolver)) { throw new TypeError('參數必須爲function'); }; // 若是直接調用Promise而非經過new關鍵詞建立,一樣拋出TypeError錯誤 if (this instanceof Promise) { initPromise(this, resolver) } else { throw new TypeError('Promise不能夠直接做爲函數調用') } }
當參數傳遞正確時,才真正初始化Promise
對象,我提到了一個單獨的函數initPromise
中,以下:數組
function initPromise(promise, resolver){ // 當調用傳入的resolver函數拋出異常,則reject當前promise try { resolver(function(value){ // 封裝的內部函數,處理resolve(value),也就是Promise Resolution Procedure resolve(promise, value); }, function(reason){ // 封裝的內部函數,處理reject(reason) reject(promise, reason); }); } catch (e) { reject(promise, e); } }
當咱們執行new Promise((resolve){resolve(5)})
時,會走resolve(promise, value)
。接下來咱們實現一下Promise Resolution Procedure
。promise
function resolve(promise, value){ // 2.3.1 若是promise和value指向同一對象 if (promise === value) { reject(promise, new TypeError('不能夠resolve Promise實例自己')) // 2.3.2 若是value是一個promise } else if (value instanceof Promise) { // 2.3.2.2 若是value處於fulfilled狀態,則使用相同的value值fulfill promise。 if (value._status == FULFILLED) { fulfill(promise, value._result); // 2.3.2.3 若是value處於rejected狀態,則使用相同的reason值reject promise。 } else if (value._status == REJECTED) { reject(promise, value._result); // 2.3.2.1 若是value處於pending狀態,則promise一樣pending並直到value狀態改變。 // 從新把resolve(promise, value)添加到隊列,asyncCall封裝了一下異步調用 } else { asyncCall(resolve, [promise, value]); } // 2.3.3 若是x是一個object或function } else if (isObjectOrFunction(value)){ // 2.3.3.2 若是獲取value.then的值時拋出異常,則經過該異常reject promise try{ let then = value.then; // 2.3.3.1 使then等於value.then // 2.3.3.3 若是then是一個函數 if (isFunction(then)) { try{ handleThenable(promise, value, then); } catch (e) { reject(promise, e); } // 2.3.3.4 若是then不是一個函數 } else { fulfill(promise, value); } } catch (e) { reject(promise, e); } // 2.3.4 value不是對象或函數 } else { fulfill(promise, value); } }
由於value.then
是函數時,處理狀況一樣不少且比較雜亂,我單獨把這部分處理提取到handleThenable
函數中。實現以下:異步
function handleThenable(promise, value, then){ let settled = false; // 是否fulfilled或rejected try { // 2.3.3.3 若是then是一個函數,則把value做爲函數中this指向來調用它 then.call(value, (otherValue)=>{ // 2.3.3.3.3 if (settled) { return}; // 2.3.3.3.1 若是resolvePromise經過傳入y來調用,則執行resolve(promise, y) resolve(promise, otherValue); settled = true; }, (reason)=>{ // 2.3.3.3.3 if (settled) { return}; // 2.3.3.3.2 若是rejectPromise 經過傳入緣由r來調用,則傳入r來reject promise reject(promise, reason); settled = true; }) // 2.3.3.3.4 若是調用then拋出異常e } catch (e) { // 2.3.3.3.4.1 若是resolvePromise或rejectPromise已經調用,則忽略 if (settled) { return}; settled = true; // 2.3.3.3.4.2 不然,則傳入e來reject promise reject(promise, e) } }
以上,基本就是完整的我對Promise Resolution Procedure
的實現。
還有一個很是重要的方法就是then
方法,接下來咱們看一下它是如何實現的。then
內部整體上分爲兩種狀況,一種是當前promise
對象狀態已經變爲fulfilled
或rejected
,此時則直接把響應的回調函數添加到異步隊列中,另外一種狀況是當前promise
對象狀態仍是pending
,此時則把響應的回調函數依次添加到數組中。async
then(onFulfilled, onRejected){ let child = new Promise(noop); // 若是當前對象狀態已經改變,則直接根據狀態調用響應的回調 if (this._status !== PENDING) { if (this._status == FULFILLED) { // 2.2.4 異步調用 asyncCall(()=>{ dealThen(this, child, onFulfilled); }) } else { // 2.2.4 異步調用 asyncCall(()=>{ dealThen(this, child, onRejected); }) } // 若是當前對象處於pending狀態,則把onFulfilled, onRejected添加到 } else { this._childArr.push(child); this._fulfillArr.push(onFulfilled); this._rejectArr.push(onRejected); } // 返回一個新的promise對象 return child; }
具體處理邏輯我放到了一個新的函數dealThen
中,注意它是異步調用的。因此用asyncCall
方法包裝了一下。函數
// 處理then function dealThen(promise, child, x){ // onFulfilled或onRejected是一個函數 if (isFunction(x)) { // 2.2.7.1 若是onFulfilled或onRejected返回了一個值value,則執行resolve(child, value) try { resolve(child, x(promise._result)); // 2.2.7.2 若是onFulfilled或onRejected拋出了異常e,則reject child並傳入緣由e } catch (e) { reject(child, e); } } else { try{ // 2.2.1.1 若是onFulfilled不是一個函數,則忽略 if (promise._status == FULFILLED) { fulfill(child, promise._result); // 2.2.1.2 若是onRejected不是一個函數,則忽略 } else { reject(child, promise._result); } } catch (e) { reject(child, e); } } }
從上面的代碼中咱們看到有兩個比較重要的方法——fulfill
和reject
,它們纔是真正改變promise
狀態並調用相應回調的地方。oop
function fulfill(promise, value){ // 若是狀態已經不是pending,則直接return if (promise._status !== PENDING) { return }; // 設置狀態爲fulfilled,並設置最終結果 promise._status = FULFILLED; promise._result = value; // 異步依次調用添加的onFulfilled方法 if (promise._fulfillArr.length > 0) { // 2.2.6.1 若是promise fulfilled,則全部的onFulfilled回調函數按照它們添加的順序依次調用。 promise._fulfillArr.forEach((k,index)=>{ // 2.2.5 onFulfilled和onRejected必須做爲函數來調用,沒有this值 asyncCall(dealThen, [promise, promise._childArr[index], k]) }); } } function reject(promise, reason){ // 若是狀態已經不是pending,則直接return if (promise._status !== PENDING) { return }; // 設置狀態爲rejected,並設置最終結果 promise._status = REJECTED; promise._result = reason; // 異步依次調用添加的onRejected方法 if (promise._rejectArr.length > 0) { // 2.2.6.2 若是promise rejected,則全部的onRejected回調函數按照它們添加的順序依次調用。 promise._rejectArr.forEach((k,index)=>{ // 2.2.5 onFulfilled和onRejected必須做爲函數來調用,沒有this值 asyncCall(dealThen, [promise, promise._childArr[index], k]) }); } }
固然,還有一個實例方法catch
,其實它調用的也是then
方法。學習
catch(onRejected){ return this.then(undefined, onRejected); }
固然,Promise
還有四個實例方法,分別以下:
resolve
Promise.resolve = function(value){ return new Promise(function(resolve){resolve(value)}) }
reject
Promise.reject = function(reason){ return new Promise(function(resolve, reject){reject(reason)}) }
all和race的實現沒有太好好思考,也沒有跑測試,不知道有沒有問題,並且我的感受實現的也比較挫,是用setInterval
一直輪詢查看每個promise
實例狀態有沒有改變,因此就不show code了。主要內容仍是講Promises/A+
的實現。
完整的代碼見個人github。
固然也有幾個問題,異步我是用setTimeout
實現的,它屬於macro-task
,而原生的Promise
屬於micro-task
,因此這裏還有待改進。
另外,在上面的實現中,咱們發現resolve(promise, value)
中,在對2.3.2.1 若是value處於pending狀態,則promise一樣pending並直到value狀態改變。我基本採用的也是setTimeout
輪詢的方式實現的。以後看了es-promise
的實現,由於根據2.3.2.1此時promise
的狀態和value
是同步的,因此能夠把resolve
和reject
promise
分別放在value
相應狀態的回調中,並假設此時與之對應的value
的child
是undefined
。以下所示:
// asyncCall(resolve, [promise, value]); value._childArr.push(undefined); value._fulfillArr.push((value)=>{resolve(promise, value)}); // ① value._rejectArr.push((reason)=>{reject(promise, reason)}); // ②
此時咱們還須要對fulfill
和reject
兩個方法稍做改動。
function fulfill(promise, value){ if (promise._status !== PENDING) { return }; promise._status = FULFILLED; promise._result = value; if (promise._fulfillArr.length > 0) { promise._fulfillArr.forEach((k,index)=>{ // 若是對應的child不是undefined,則異步調用回調 if (promise._childArr[index]) { asyncCall(dealThen, [promise, promise._childArr[index], k]) // 若是對應的child是undefined,則直接執行回調函數①並傳入value } else { k(value); } }); } } function reject(promise, reason){ if (promise._status !== PENDING) { return }; promise._status = REJECTED; promise._result = reason; if (promise._rejectArr.length > 0) { promise._rejectArr.forEach((k,index)=>{ // 若是對應的child不是undefined,則異步調用回調 if (promise._childArr[index]) { asyncCall(dealThen, [promise, promise._childArr[index], k]) // 若是對應的child是undefined,則直接執行回調函數②並傳入reason } else { k(reason); } }); }; }
修改版見Promise1。
錯誤或不足之處,歡迎指正。