這篇文章內容主要來自一篇stack Overflow高票答案javascript
聲明:此Promise的實現僅僅是爲了加深本人對其的理解,和A+規範有些出入,可是的確是目前看過全部promise代碼中最漂亮,思路比較清晰的一個。
文章不會特地幫助讀者複習Promise基本操做。java
Promise其實本質上就是一個狀態機,因此首先咱們描述一個靜態的狀態機,就像下邊這樣git
var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; function Promise() { // 存儲的狀態是上邊的三個:執行中,已完成,已拒絕 var state = PENDING; // 存儲異步結果或者異步錯誤消息 var value = null; // 負責處理中途加入的處理函數 var handlers = []; }
完成了基本的狀態機定義,接下來的問題就是完成「狀態改變」這個動做的實現:github
var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; function Promise() { // 存儲三個狀態 var state = PENDING; // 一旦出現狀態的改變,異步結果就會被存到這個地方 var value = null; // 存儲成功或者失敗的handler var handlers = []; //狀態轉移到成功 function fulfill(result) { state = FULFILLED; value = result; } //狀態轉移到失敗 function reject(error) { state = REJECTED; value = error; } }
到目前爲止,咱們給出了兩個很純粹的變化動做,在開發的過程當中這兩個動做會很很差用,因此咱們在這兩個動做的基礎上構建一個高層次的動做(其實就是加點判斷而後封裝一層),就像下邊這兒,名字就叫作resolve,可是注意和咱們正常使用promise調用的那個resolve並不同,不要搞混:promise
var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; function Promise() { var state = PENDING; var value = null; var handlers = []; function fulfill(result) { state = FULFILLED; value = result; } function reject(error) { state = REJECTED; value = error; } //這裏暫時缺乏兩個重要函數getThen和doResolve這兩個函數,稍後會說道 function resolve(result) { try { var then = getThen(result); //判斷then是否是一個Promise對象 if (then) { doResolve(then.bind(result), resolve, reject) return } fulfill(result); } catch (e) { reject(e); } } }
是的,咱們的用到了兩個輔助函數getThen和doResolve,如今給出實現:異步
/** * 這裏會判斷value的類型,咱們只要promise.then這個函數,其餘的通通返回null * * @param {Promise|Any} value * @return {Function|Null} */ function getThen(value) { var t = typeof value; if (value && (t === 'object' || t === 'function')) { var then = value.then; if (typeof then === 'function') { return then; } } return null; } /** * 這個函數的主要做用就是串主邏輯,完成「變化狀態」這個動做 * * @param {Function} fn A resolver function that may not be trusted * @param {Function} onFulfilled * @param {Function} onRejected */ function doResolve(fn, onFulfilled, onRejected) { //done的做用是讓onFulfilled或者onRejected僅僅被調用一次,狀態機狀態一旦改變無法回頭 var done = false; try { //在咱們正常使用Promise的時候調的resolve,其實用的就是這裏fn注入函數而後調用 fn(function (value) { if (done) return done = true **onFulfilled(value)** }, function (reason) { if (done) return done = true onRejected(reason) }) } catch (ex) { if (done) return done = true onRejected(ex) } }
好了,一個完整的狀態機已經完成,咱們完成了一個基本的狀態變化邏輯,接下來要作的就是一步一步的朝promise標準進發,這個promise缺乏什麼呢,暫時缺的就是初始的動做啦(new promise(func)對象一旦被初始化內部代碼當即執行),因此咱們加上初始動做的開啓async
var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; function Promise(fn) { var state = PENDING; var value = null; var handlers = []; function fulfill(result) { state = FULFILLED; value = result; } function reject(error) { state = REJECTED; value = error; } function resolve(result) { try { var then = getThen(result); if (then) { doResolve(then.bind(result), resolve, reject) return } fulfill(result); } catch (e) { reject(e); } } //開啓任務的執行,因此我說doResolve其實才是「主線任務」的引子,而fn其實就是你寫的代碼 doResolve(fn, resolve, reject); }
咱們實現了狀態機,可是目前的問題是咱們只能眼睜睜的看着代碼的流動直到一個Promise結束爲止,即無法添加也無法獲取結果,這就有很大的侷限性了,因此咱們要使用then方法來串聯Promise,用done方法來完成結果的收集,首先實現done方法,由於then其實說白了就是收集上邊的結果--完成本身的邏輯--把結果傳遞給下一個Promise,作的事情是done的超集。函數
var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; function Promise(fn) { var state = PENDING; var value = null; var handlers = []; function fulfill(result) { state = FULFILLED; value = result; //專門封裝一個handle函數處理後續邏輯,在下面有this.handle(handler)方法 handlers.forEach(handle); //在狀態變成已處理而且以前加入的handler都被處理完畢的狀況下再加入handler就會報錯而且沒有卵用 handlers = null; } function reject(error) { state = REJECTED; value = error; handlers.forEach(handle); handlers = null; } function resolve(result) { try { var then = getThen(result); if (then) { doResolve(then.bind(result), resolve, reject) return } fulfill(result); } catch (e) { reject(e); } } function handle(handler) { if (state === PENDING) { handlers.push(handler); } else { if (state === FULFILLED && typeof handler.onFulfilled === 'function') { handler.onFulfilled(value); } if (state === REJECTED && typeof handler.onRejected === 'function') { handler.onRejected(value); } } } //注意看下面done方法的實現,裏邊只有一個異步方法,換句話說就是會當即返回不會產生阻塞,咱們以後會在then當中調用done方法,這裏的onFulfilled, onRejected就是用戶寫的處理函數,promise異步的特性就是這樣來的。 this.done = function (onFulfilled, onRejected) { // ensure we are always asynchronous setTimeout(function () { handle({ onFulfilled: onFulfilled, onRejected: onRejected }); }, 0); } doResolve(fn, resolve, reject); }
最後,咱們來實現Promise.then,完成狀態機的串聯:性能
//這段代碼有點繞,主要須要完成的工做其實就是,判斷上一個Promise是否完成,而後執行用戶的then裏邊的回調代碼,並最終返回一個新的Promise,而後依次循環。。。 this.then = function (onFulfilled, onRejected) { //開啓then以後就會返回一個新的promise,可是這個時候咱們還可能有上一個Promise的任務沒有完成,因此先把上邊一個promise對象的this指向保存下來 var self = this; //返回一個新包裝Promise,這和咱們普通的在外邊寫new Promise是一個道理 return new Promise(function (resolve, reject) { //done的代碼一樣是當即返回,而後異步執行的 return self.done(function (result) { if (typeof onFulfilled === 'function') { try { return resolve(onFulfilled(result)); } catch (ex) { return reject(ex); } } else { return resolve(result); } }, function (error) { if (typeof onRejected === 'function') { try { return resolve(onRejected(error)); } catch (ex) { return reject(ex); } } else { return reject(error); } }); }); }
Over
更多參考請看下面:
簡單的實現Promsie
高性能實現Promise,以及專門的wikithis