這篇文章旨在解析 Promise的異步實現原理,而且以 ES6中的 Promise 爲藍本實現一個簡單的 Promise。git
經過本身動手實現一個 Promise 對象,能夠熟悉不少可能不知道的 Promise 細節,同時也能對異步的理解更提高一步。es6
本文假設讀者對 Promise 規範有必定理解,而且熟悉 ES6 中的 Promise 基本操做。github
Promise 規範中規定了,promise 的狀態只有3種:promise
顧名思義,對上面3個狀態的解釋就再也不贅述,Promise 的狀態一旦改變則不會再改變。瀏覽器
具體的規範能夠參見:https://promisesaplus.comdom
這部份內容參考: http://es6.ruanyifeng.com/#do...異步
作了上面的鋪墊,實現一個 Promise 的思路就清晰不少了,本文使用 ES6 來進行實現,暫且把這個類取名爲 GPromise吧(不覆蓋原生的,便於和原生進行對比測試)。下文中 GPromise 代指將要實現的類,Promise 代指 ES6中的 Promise 類。async
在瀏覽器中打印出一個 Promise 實例會發現其中會包括兩用"[[ ]]"包裹起來的屬性,這是系統內部屬性,只有JS 引擎可以訪問。函數
[[PromiseStatus]] [[PromiseValue]]
以上兩個屬性分別是 Promise 對象的狀態和最終值。測試
咱們本身不能實現內部屬性,JS中私有屬性特性(#修飾符如今仍是提案)暫時也沒有支持,因此暫且用"_"前綴規定私有屬性,這樣就模擬了Promise 中的兩個內部屬性。
class GPromise { constructor(executor) { this._promiseStatus = GPromise.PENDING; this._promiseValue; this.execute(executor); } execute(executor){ //... } then(onfulfilled, onrejected){ //... } } GPromise.PENDING = 'pedding'; GPromise.FULFILLED = 'resolved'; GPromise.REJECTED = 'rejected';
execute(executor) { if (typeof executor != 'function') { throw new Error(` GPromise resolver ${executor} is not a function`); } //捕獲錯誤 try { executor(data => { this.promiseStatus = GPromise.FULFILLED; this.promiseValue = data; }, data => { this.promiseStatus = GPromise.REJECTED; this.promiseValue = data; }); } catch (e) { this.promiseStatus = GPromise.REJECTED; this.promiseValue = e; } }
注:Promise 對象在executor 發生錯誤或者reject 時,若是沒有then
或者 catch 來處理,會把錯誤拋出到外部,也就是會報錯。GPromise 實現的是沒有向外部拋出錯誤,只能由then方法處理。
then 方法內部邏輯稍微複雜點,而且有一點必定必定必定要注意到: then 方法中的回調是異步執行的,思考下下段代碼:
console.log(1); new Promise((resolve,reject)=>{ console.log(2); resolve(); }) .then(()=>console.log(3)); console.log(4);
執行結果是什麼呢?答案實際上是:1 2 4 3。傳入Promise 中的執行函數是當即執行完的啊,爲何不是當即執行 then 中的回調呢?由於then 中的回調是異步執行,表示該回調是插入事件隊列末尾,在當前的同步任務結束以後,下次事件循環開始時執行隊列中的任務。
then 方法中的難點就是處理異步,其中一個方案是經過 setInterval來監聽GPromise 對象的狀態改變,一旦改變則執行相應then 中相應的回調函數(onfulfilled和onrejected),這樣回調函數就可以插入事件隊列末尾,異步執行,實驗證實可行,這種方案是最直觀也最容易理解的。
then 方法的返回值是一個新的 GPromise 對象,而且這個對象的狀態和 then 中的回調返回值相關,回調指代傳入的 onfulfilled 和 rejected。
then 方法中的重點邏輯如上,其餘參見代碼便可:
then(onfulfilled, onrejected) { let _ref = null, timer = null, result = new GPromise(() => {}); //由於 promise 的 executor 是異步操做,須要監聽 promise 對象狀態變化,而且不能阻塞線程 timer = setInterval(() => { if ((typeof onfulfilled == 'function' && this._promiseStatus == GPromise.FULFILLED) || (typeof onrejected == 'function' && this._promiseStatus == GPromise.REJECTED)) { //狀態發生變化,取消監聽 clearInterval(timer); //捕獲傳入 then 中的回調的錯誤,交給 then 返回的 promise 處理 try { if (this._promiseStatus == GPromise.FULFILLED) { _ref = onfulfilled(this._promiseValue); } else { _ref = onrejected(this._promiseValue); } //根據回調的返回值來決定 then 返回的 GPromise 實例的狀態 if (_ref instanceof GPromise) { //若是回調函數中返回的是 GPromise 實例,那麼須要監聽其狀態變化,返回新實例的狀態是根據其變化相應的 timer = setInterval(()=>{ if (_ref._promiseStatus == GPromise.FULFILLED || _ref._promiseStatus == GPromise.REJECTED) { clearInterval(timer); result._promiseValue = _ref._promiseValue; result._promiseStatus = _ref._promiseStatus; } },0); } else { //若是返回的是非 GPromise 實例 result._promiseValue = _ref; result._promiseStatus = GPromise.FULFILLED; } } catch (e) { //回調中拋出錯誤的狀況 result._promiseStatus = GPromise.REJECTED; result._promiseValue = e; } } }, 0); //promise 之因此可以鏈式操做,由於返回了GPromise對象 return result; }
是騾子是馬,拉出來溜溜。。
測試環境是macOS Sierra 10.12.6,Chrome 60.0.3112.113。
通過如下測試, 證實了GPromise 的基本的異步流程管理和原生 Promise 沒有差異。如下測試用例參考了 MDN 中的[Promise
API](https://developer.mozilla.org... 中的 Advanced Example。
var promiseCount = 0; function test(isPromise) { let thisPromiseCount = ++promiseCount, executor = (resolve, reject) => { console.log(thisPromiseCount + ') Promise started (Async code started)'); window.setTimeout( function () { resolve(thisPromiseCount); }, Math.random() * 2000 + 1000); }; console.log(thisPromiseCount + ') Started (Sync code started)'); let p1 = isPromise ? new Promise(executor) : new GPromise(executor); p1.then( function (val) { console.log(val + ') Promise fulfilled (Async code terminated)'); }, function (reason) { console.log('Handle rejected promise (' + reason + ') here.'); }); console.log(thisPromiseCount + ') Promise made (Sync code terminated)'); } test(); test(true); test();
那麼再來測試下鏈式操做(沒有鏈式操做的 Promise 我要你有何用?),測試結果和 Promise 表現一致。
function async1() { return new GPromise( (resolve, reject) => { console.log('async1 start'); setTimeout(() => { resolve('async1 finished') }, 1000); } ); } function async2() { return new GPromise( (resolve, reject) => { console.log('async2 start'); setTimeout(() => { resolve('async2 finished') }, 1000); } ); } function async3() { return new GPromise( (resolve, reject) => { console.log('async3 start'); setTimeout(() => { resolve('async3 finished'); }, 1000); } ); } async1() .then( data => { console.log(data); return async2(); }) .then( data => { console.log(data); return async3(); } ) .then( data => { console.log(data); } );
到此爲止,一個高仿的 Promise 已經實現完成了,它很簡單,由於只有一個 then 方法,異步的狀態管理由內部完成。
這裏並無實現 catch方法,由於上文也提到了,catch方法就至關於 then(null,onrejected) 。並且 Promise 類上的 race,all,resolve,reject也沒有實現,本文旨在理清 Promise 核心原理,篇幅受限(其實就是我懶),其餘輔助類的方法等以後有時間再實現。
本文提供的只是一個思路,但願能幫助到你,歡迎你們批評指教。
代碼地址:Github