若是要手寫實現promise,那麼先看看promise/A+規範,再來實現,將會事半功倍。
那麼我先翻譯一下Promise/A+規範中的內容。git
要求
2.1 promise 狀態 github
一個promise必須是三種狀態其中的一種狀態:pending,fulfilled 或者 rejected。 算法
2.1.1 當promise處於pending狀態時:npm
2.1.1.1 能夠轉變到 fulfilled 或者 rejected 狀態。
2.1.2 當promise處於fulfilled狀態時:promise
2.1.2.1 必定不可以轉變到其餘任何一種狀態。 2.1.2.2 必須有一個value,而且這個值必定不能改變。
2.1.3 當promise處於rejected狀態時:異步
2.1.3.1 必定不可以轉變到其餘任何一種狀態。 2.1.3.2 必須有一個reason,而且這個值不可以改變。
在這裏,「必定不能改變」意味着不可變的身份(即===),但並不意味着深層不變性。(我的理解爲是value/reason指向的地址是不可變的,但倘若value/reason爲一個對象,則對象內的值是可變的。)函數
2.2 then 方法
一個promise必須提供一個then方法去訪問當前或者最終的value或者reason。
一個promise的then方法接受兩個參數:測試
promise.then(onFulfilled, onRejected)
2.2.1 onFulfilled 和 onRejected 都是可選的參數:this
2.2.1.1 若是 onFulfilled 不是一個函數,它必須被忽略。 2.2.1.2 若是 onRejected 不是一個函數,它必須被忽略。
2.2.2 若是 onFulfilled 是一個函數:spa
2.2.2.1 promise 是 fulfilled 狀態時它必須被調用,而且 promise 的 value 做爲它的第一個參數。 2.2.2.2 它必定不能在 promise 進入 fulfilled 狀態以前被調用。 2.2.2.3 它必定不可以調用超過一次。
2.2.3 若是 onRejected 時一個函數:
2.2.3.1 promise 是 rejected 狀態時它必須被調用,而且 promise 的 reason 做爲它的第一個參數。 2.2.3.2 它必定不能在 promise 進入 rejected 狀態以前被調用。 2.2.3.3 它必定不可以調用超過一次。
2.2.4 直到執行上下文堆棧僅包含平臺代碼以前,onFulfilled 或 onRejected 不可以被調用。[3.1]
2.2.5 onFulfilled 和 onRejected 必須以函數的形式被調用(即沒有this值)。[3.2]
2.2.6 then 能夠在同一個promise被調用屢次:
2.2.6.1 當 promise 處於 fulfilled 狀態時,各個 onFulfilled 回調必須按其原始調用的順序執行。 2.2.6.2 當 promise 處於 rejected 狀態時,各個 onRejected 回調必須按其原始調用的順序執行。
2.2.7 then 必須返回一個promise [3.3]:
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 若是 onFulfilled 或 onRejected 返回一個值 x,運行Promise解決程序 [[Resolve]](promise2, x)。 2.2.7.2 若是 onFulfilled 或 onRejected 拋出一個意外 e,promise2 必須以 e 爲 reason 被 rejected。 2.2.7.3 若是 onFulfilled 不是一個函數而且 promise1 處於 fulfilled 狀態,promise2 必須以與 promise1 一樣的 value 轉變到 fulfilled 狀態。 2.2.7.4 若是 onRejected 不是一個函數而且 promise1 處於 rejected狀態,promise2 必須以與 promise1 一樣的 reason 轉變到 rejected狀態。
2.3 Promise解決程序
promise解決程序是一個抽象的操做,它把一個 promise 和一個 value 做爲輸入,咱們將這個表示爲 [[Resolve]](promise, x)。若是 x 是一個 thenable ,它將會試圖讓 promise 採用 x 的狀態,前提是x的行爲至少有點像一個 promise。不然,它將會用值 x 執行 promise。
對這些 thenable 的處理使得與 promise 實現方式可以去互相操做。只要它們公開了符合 Promise/A+ 的 then 方法。它還使得 promises/A+ 實現方式可以採用合理的 then 方法去「同化」不一致的實現方式。
爲了運行[[Resolve]](promise, x),執行如下步驟:
2.3.1 若是 promise 與 x 是同一個對象,以 Tyeperror 做爲 reason 去 reject promise。
2.3.2 若是 x 是一個 promise,使用它的狀態:
2.3.2.1 若是 x 處於 pending 狀態,promise 必須保持 pending 狀態直到 x 處於 fulfilled 或者 rejected 狀態。 2.3.2.2 若是 x 處於 fulfilled 狀態,以相同的 value 去 fulfill promise。 2.3.2.3 若是 x 處於 rejected 狀態,以相同的 reason 去 reject promise。
2.3.3 不然,若是 x 是一個對象或者函數:
2.3.3.1 讓 then 做爲 x.then。 2.3.3.2 若是取屬性 x.then 會致使拋出異常 e,則以 e 爲 reason reject promise。 2.3.3.3 若是 then 是一個函數,讓 x 做爲 this 調用它,第一個參數爲 resolvePromise,第二個參數爲 rejectPromise,而後: 2.3.3.3.1 若是使用value y 調用 resolvepromise 時,運行[[Resolve]](promise, y)。 2.3.3.3.2 若是使用reason r 調用 rejectPromise 時,也用 r reject promise。 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.2 不然,把 e 做爲 reason reject promise。 2.3.3.4 若是 then 不是一個函數,將 x 做爲參數執行 promise。
2.3.4 若是 x 不是一個對象或者函數,將 x 做爲參數執行 promise。
若是一個參與了 thenable 循環鏈的 thenable 去 resolve promise,這樣 [[Resolve]](promise, thenable) 的遞歸性質最終會致使 [[Resolve]](promise, thenable) 會被再次調用,遵循上述算法將會致使無限遞歸。咱們鼓勵去實現(但不是必需的)檢測這樣的遞歸,並以 TypeError 做爲 reason 去 reject Promise。[3.6]
接下來根據規範進行手寫實現,註釋偷懶就將對應的規範標註出來,其實基本上就是對着規範實現。
function promise(fuc){ //接收一個函數做爲參數 let that = this; that.value = null; // 2.1.2.2 that.reason = null;// 2.1.3.2 that.onFulfilled = []; // 2.2.6 that.onRejected = []; //2.2.6 that.status = 'pending'; // 2.1 function resolve(val){ if (that.status === 'pending') { // 2.1.2 that.status = 'fulfilled'; that.value = val; that.onFulfilled.forEach(fc => fc(val)); //2.2.6.1 } } function reject(err){ if (that.status === 'pending') { //2.1.3 that.status = 'rejected'; that.reason = err; that.onRejected.forEach(fc => fc(err)); //2.2.6.2 } } try { fuc(resolve,reject); } catch (error) { reject(error); } } promise.prototype.then = function (onFulfilled, onRejected) //2.2 { let that = this, promise2; //2.2.7 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (x) => x; //2.2.1 2.2.7.3 onRejected = typeof onRejected === 'function' ? onRejected : (e) => { throw e }; // 2.2.7.4 switch (that.status) { case 'pending': promise2 = new promise((reslove, reject)=>{ that.onFulfilled.push(()=>{ setTimeout(() => { try { let x = onFulfilled(that.value); //2.2.2.1 ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1 } catch (err) { reject(err); //2.2.7.2 } }, 0); }); that.onRejected.push(()=>{ setTimeout(() => { try { let x = onRejected(that.reason); //2.2.3.1 ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1 } catch (err) { reject(err); //2.2.7.2 } }, 0); }); }) break; case 'fulfilled': promise2 = new promise((reslove,reject)=>{ setTimeout(() => { // 2.2.4 try{ let x = onFulfilled(that.value); //2.2.2.1 ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1 }catch(err){ reject(err); //2.2.7.2 } }, 0); }) break; case 'rejected': promise2 = new promise((reslove, reject)=>{ setTimeout(() => { // 2.2.4 try{ let x = onRejected(that.reason); //2.2.3.1 ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1 }catch(err){ reject(err); //2.2.7.2 } }, 0); }) break; default: break; } return promise2; } function ResolutionProcedure(promise, x, reslove, reject){ //2.3 if (promise === x) //2.3.1 return reject(new TypeError("same promise")); if (x instanceof Promise) //2.3.2 x.then(((xvalue)=>{ ResolutionProcedure(promise, xvalue, reslove, reject); },(xreason)=>{ reject(xreason) })); if (x !== null &&( typeof x === 'object' || typeof x === 'function' )) //2.3.3 { try { let then = x.then;//2.3.3.1 if(typeof then === 'function') //2.3.3.3 { let isuse = false; try { then.call(x,(y)=>{ if (!isuse) { ResolutionProcedure(promise, y, reslove, reject); //2.3.3.3.1 isuse = true; } },(r)=>{ if (!isuse) { reject(r) //2.3.3.3.2 isuse = true; } }) } catch (error) { if(!isuse) reject(error) //2.3.3.3.4.2 } }else{ reslove(x); //2.3.3.4 } } catch (error) { reject(error); //2.3.3.2 } }else{ reslove(x); //2.3.4 } }
作完以後能夠經過 promise test 進行測試。
先在代碼下面加上一些測試須要代碼。
promise.deferred = function () { let def = {}; def.promise = new promise(function (resolve, reject) { def.resolve = resolve; def.reject = reject; }); return def; } module.exports = promise
而後跑一下下面的代碼便可。
npm install -g promises-aplus-tests promises-aplus-tests promise.js
結果所有經過,說明符合規範: