更多精彩內容,歡迎關注微信公衆號~
異步編程是前端開發者必需的技能,過去管理異步的主要機制都是經過函數回調,然而會出現像「回調地獄」這樣的問題。爲了更好的管理回調,ES6 增長了一個新的特性 Promise。Promise 是 ES7 中async/await 語法的基礎,是 JavaScript 中處理異步的標準形式,現實開發中基本離不開 Promise 了。咱們接下來會根據 Promises/A+ 規範本身實現一個 Promise。前端
完整的代碼能夠點擊個人 github 進行查看,若是你喜歡,歡迎 star,若是發現有問題或者錯誤,也歡迎提出來。git
首先咱們來看 Promises/A+ 規範的具體內容,Promises/A+ 規範能夠查看 Promises/A+,下面是翻譯的規範供參考github
Promise 必須處於如下三個狀態之一: pending, fulfilled 或者 rejected。npm
2.1.1.1 能夠變成 fulfilled 或者是 rejected
編程
2.1.2.1 不會變成其它狀態
promise
2.1.2.2 必須有一個value值
緩存
2.1.3.1 不會變成其它狀態
微信
2.1.3.2 必須有一個 promise 被 reject 的 reason
異步
promise 必須提供一個 then 方法,來訪問最終的結果async
promise 的 then 方法接收兩個參數
promise.then(onFulfilled, onRejected)
2.2.1.1 onFulfilled 必須是函數類型
2.2.1.2 onRejected 必須是函數類型
2.2.2.1 必須在 promise 變成 fulfilled 時,調用 onFulfilled,參數是 promise 的 value
2.2.2.2 在 promise 的狀態不是 fulfilled 以前,不能調用
2.2.2.3 onFulfilled 只能被調用一次
2.2.3.1 必須在promise變成 rejected 時,調用 onRejected,參數是promise的reason
2.2.3.2 在promise的狀態不是 rejected 以前,不能調用
2.2.3.3 onRejected 只能被調用一次
2.2.6.1 若是 promise 變成了 fulfilled 態,全部的 onFulfilled 回調都須要按照 then 的順序執行
2.2.6.2 若是 promise 變成了 rejected 態,全部的 onRejected 回調都須要按照 then 的順序執行
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 onFulfilled 或 onRejected 執行的結果爲 x, 執行 resolutionProcedure(promise2, x)
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
resolutionProcedure(promise2, x, resolve, reject)
2.3.2.1 若是 x 是 pending 狀態,那麼 promise 必需要保持 pending 狀態直到 x 變成 fulfilled 或者 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.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, resolvePromise, rejectPromise)
2.3.3.3.1 resolvePromiseFn 的 入參是 y, 執行
resolutionProcedure(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.2 不然,reject promise with e as the reason
2.3.3.4 若是 then 不是一個function. fulfill promise with x.
接下來咱們只要按照規範來實現 Promise 就好了,很顯然規範後半部分看起來是比較複雜的,尤爲是 resolutionProcedure 函數的實現,但其實這只是一些實現的細節而已。初看可能不是那麼順暢,那麼強烈建議多看幾遍規範,而後本身多實現幾遍。
1. promise 須要傳遞一個 executor 執行器,執行器馬上執行
2. 執行器 executor 接受兩個參數,分別是 resolve 和 reject
3. promise 只能從 pending 到 rejected, 或者從 pending 到 fulfilled,狀態一旦確認,就不會再改變
4. promise 都有 then 方法,then 接收兩個參數,分別是 promise 成功的回調 onFulfilled, 和 promise 失敗的回調 onRejected
6. 若是調用 then 時,promise已經成功,則執行 onFulfilled,並將 promise 的值做爲參數傳遞進去。 若是 promise 已經失敗,那麼執行 onRejected, 並將 promise 失敗的緣由做爲參數傳遞進去。若是 promise 的狀態是 pending,須要將 onFulfilled 和 onRejected 函數存放起來,等待狀態肯定後,再依次將對應的函數執行。
7. then 的參數 onFulfilled 和 onRejected 能夠缺省
8. promise 能夠 then 屢次,promise 的 then 方法返回一個新的 promise
9. 若是 then 返回的是一個結果,那麼就會把這個結果做爲參數,傳遞給下一個 then 的成功的回調 onFulfilled
10. 若是 then 中拋出了異常,那麼就會把這個異常做爲參數,傳遞給下一個 then 的失敗的回調 onRejected
11. 若是 then 返回的是一個 promise, 那麼須要等這個 promise,那麼會等這個 promise 執行完。promise 若是成功, 就走下一個 then 的成功,若是失敗,就走下一個 then 的失敗
// 定義三種狀態的常量 const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; // pomise 接收一個 executor 執行器,執行器馬上執行 function MyPromise(executor) { const _this = this; // promise 當前的狀態 _this.currentState = PENDING; _this.value = undefined; // 保存 then 中的回調,只有當 promise 狀態爲 pending 時纔會緩存,而且每一個實例至多緩存一個 _this.onFulfilledCallbacks = []; _this.onRejectedCallbacks = []; function resolve(value) { if (value instanceof MyPromise) { // 若是 value 是個 Promise,調用 then 方法繼續執行 value.then(resolve, reject); } // 異步執行,保證執行順序 setTimeout(() => { if (_this.currentState === PENDING) { _this.currentState = FULFILLED; _this.value = value; _this.onFulfilledCallbacks.forEach(fn => fn()); } }); } function reject(reason) { // 異步執行,保證執行順序 setTimeout(() => { if (_this.currentState === PENDING) { _this.currentState = REJECTED; _this.value = reason; _this.onRejectedCallbacks.forEach(fn => fn()); } }); } try { executor(resolve, reject); } catch(err) { reject(err); } } MyPromise.prototype.constructor = MyPromise; MyPromise.prototype.then = function (onFulfilled, onRejected) { const _this = this; // 2.2.1 onFulfilled 和 onRejected 都是可選參數 // 2.2.5 onFulfilled 和 onRejected 必須做爲函數被調用 // 2.2.7.3 若是 onFulfilled 不是一個函數,promise2 以promise1的值fulfilled // 2.2.7.4 若是 onRejected 不是一個函數,promise2 以promise1的reason rejected onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : valve => valve; onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }; // 2.2.7,then 必須返回一個新的 promise const promise2 = new MyPromise((resolve, reject) => { if (_this.currentState === FULFILLED) { // 2.2.4 保證 onFulfilled,onRjected 異步執行 setTimeout(() => { try { // 2.2.7.1 onFulfilled 或 onRejected 執行的結果爲 x, 調用 resolutionProcedure const x = onFulfilled(_this.value); resolutionProcedure(promise2, x, resolve, reject); } catch(err) { // 2.2.7.2 若是 onFulfilled 或者 onRejected 執行時拋出異常 err, promise2 須要被 reject reject(err); } }); } if (_this.currentState === REJECTED) { setTimeout(() => { try { // 2.2.7.1 onFulfilled 或 onRejected 執行的結果爲 x, 調用 resolutionProcedure const x = onRejected(_this.value); resolutionProcedure(promise2, x, resolve, reject); } catch(err) { // 2.2.7.2 若是 onFulfilled 或者 onRejected 執行時拋出異常 err, promise2 須要被 reject reject(err); } }); } if (_this.currentState === PENDING) { _this.onFulfilledCallbacks.push(() => { setTimeout(() => { try { const x = onFulfilled(_this.value); resolutionProcedure(promise2, x, resolve, reject); } catch(err) { reject(err); } }); }); _this.onRejectedCallbacks.push(() => { setTimeout(() => { try { const x = onRejected(_this.value); resolutionProcedure(promise2, x, resolve, reject); } catch(err) { reject(err); } }); }); } }); return promise2; } // 2.3 resolutionProcedure(promise2, x, resolve, reject) function resolutionProcedure(promise2, x, resolve, reject) { // 2.3.1 若是 promise2 和 x 相等,那麼 reject promise with a TypeError if (promise2 === x) { reject(new TypeError('Error')); } // 2.3.2 若是 x 爲 Promise,狀態爲 pending 須要繼續等待不然執行 if (x instanceof MyPromise) { if (x.currentState === PENDING) { x.then(value => { resolutionProcedure(promise2, value, resolve, reject); }, reject) } else { x.then(resolve, reject); } } // 2.3.3.3.3 reject 或者 resolve 其中一個執行過的話,忽略其餘的 let called = false; // 2.3.3,判斷 x 是否爲對象或者函數 if ( x && (typeof x === 'object' || typeof x === 'function') ) { try { let then = x.then; // 2.3.3.3 若是 then 是一個函數,then.call(x, resolvePromise, rejectPromise) if (typeof then === 'function') { then.call( x, y => { if (called) return; called = true; // 2.3.3.3.1 resolvePromiseFn 的 入參是 y, 執行 resolutionProcedure(promise2, y, resolve, reject); resolutionProcedure(promise2, y, resolve, reject); }, r => { if (called) return; called = true; // 2.3.3.3.2 rejectPromise 的 入參是 r, reject promise with r. reject(r); } ); } else { // 2.3.3.4 若是 then 不是一個 function. fulfill promise with x. resolve(x); } } catch(err) { // 2.3.3.2 若是 x.then 這步出錯,那麼 reject promise with err as the reason if (called) return; called = true; reject(err) } } else { // 2.3.4 若是 x 不是一個 object 或者 function,fulfill promise with x. resolve(x); } } module.exports = MyPromise;
promises-aplus-tests 這個 npm 包能夠幫助咱們測試所編寫的 promise 代碼是否符合 Promises/A+ 的規範。
不過咱們須要先增長如下代碼去提供測試的接口
MyPromise.defer = MyPromise.deferred = function () { let dfd = {}; dfd.promise = new MyPromise((resolve, reject) => { dfd.resolve = resolve; dfd.reject = reject; }); return dfd; }
經過 npm 安裝
npm install promises-aplus-tests --save-dev
經過執行 promises-aplus-tests target.js 能夠執行測試,promises-aplus-tests 中共有 872 條測試用例。
能夠在個人 github 進行查看,經過 npm run test 來執行測試,能夠經過全部的測試用例。
更多精彩內容,歡迎關注微信公衆號~