這兩天在熟悉 kissy 框架的時候,看到了
Promise
模塊。Promise
對於一個Jser並不陌生,Promise
相似於一個事務管理器,它的做用就是將各類內嵌回調的事務用流水形式表達。利用Promise
可讓異步編程更符合人的直覺,讓代碼邏輯更加清晰,把開發人員從回調地獄中釋放出來。這麼「高大上」的東西,之前寫nodejs
代碼的時候只是簡單的用用,尚未理解其基本的實現原理,罪過!我的認爲,理解編程思想最好的途徑就是閱讀一份簡易的實現源碼。很幸運,網上有很多Promise
的簡易實現,其中 這篇博文 介紹的實現方式很是贊,下面就來好好研究下吧!javascript
目前, Promise
是 ECMAScript 6
規範的重要特性之一,各大瀏覽器也開始慢慢支持這一特性。固然,也有一些第三方內庫實現了該功能,如: Q 、 when 、 WinJS 、 RSVP.js 等。html
Promise
對象用來進行延遲( deferred
)和異步( asynchronous
)計算。一個 Promise
處於如下四種狀態之一:前端
fulfilled
或 rejected
Promise
對象有兩個重要的方法,一個是 then
,另外一個是 resolve
:html5
Promise
經常使用方式以下:java
var p = new Promise(function(resolve, reject) { ... // 事務觸發 resovle(xxx); ... }); p.then(function(value) { // 知足 }, function(reason) { // 拒絕 }).then().then()...
示意圖以下:node
1. Promise
其實就是一個狀態機。按照它的定義,咱們可從以下基礎代碼開始:git
var PENDING = 0; // 進行中 var FULFILLED = 1; // 成功 var REJECTED = 2; // 失敗 function Promise() { // 存儲PENDING, FULFILLED或者REJECTED的狀態 var state = PENDING; // 存儲成功或失敗的結果值 var value = null; // 存儲成功或失敗的處理程序,經過調用`.then`或者`.done`方法 var handlers = []; // 成功狀態變化 function fulfill(result) { state = FULFILLED; value = result; } // 失敗狀態變化 function reject(error) { state = REJECTED; value = error; } }
2.下面是 Promise
的 resolve
方法實現:es6
注意: resolve
方法可接收的參數有兩種:一個普通的值/對象或者一個 Promise
對象。若是是普通的值/對象,則直接把結果傳遞到下一個對象;若是是一個 Promise
對象,則必須先等待這個子任務序列完成。github
function Promise() { ... function resolve(result) { try { var then = getThen(result); // 若是是一個promise對象 if (then) { doResolve(then.bind(result), resolve, reject); return; } // 修改狀態,傳遞結果到下一個事務 fulfill(result); } catch (e) { reject(e); } } }
兩個輔助方法:編程
/** * Check if a value is a Promise and, if it is, * return the `then` method of that promise. * * @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; } /** * Take a potentially misbehaving resolver function and make sure * onFulfilled and onRejected are only called once. * * Makes no guarantees about asynchrony. * * @param {Function} fn A resolver function that may not be trusted * @param {Function} onFulfilled * @param {Function} onRejected */ function doResolve(fn, onFulfilled, onRejected) { var done = false; try { 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); } }
3.上面已經完成了一個完整的內部狀態機,但咱們並無暴露一個方法去解析或則觀察 Promise
。如今讓咱們開始解析 Promise
:
function Promise(fn) { ... doResolve(fn, resolve, reject); }
如你所見,咱們複用了 doResolve
,由於對於初始化的 fn
也要對其進行控制。 fn
容許調用 resolve
或則 reject
屢次,甚至拋出異常。這徹底取決於咱們去保證 promise
對象僅被 resolved
或則 rejected
一次,且狀態不能隨意改變。
4.目前,咱們已經有了一個完整的狀態機,但咱們仍然沒有辦法去觀察它的任何變化。咱們最終的目標是實現 then
方法,但 done
方法彷佛更簡單,因此讓咱們先實現它。
咱們的目標是實現 promise.done(onFullfilled, onRejected)
:
onFulfilled
和 onRejected
二者只能有一個被執行,且執行次數爲一promise
鏈式調用結束promise
已經被解析,均可以調用該方法var PENDING = 0; // 進行中 var FULFILLED = 1; // 成功 var REJECTED = 2; // 失敗 function Promise() { // 存儲PENDING, FULFILLED或者REJECTED的狀態 var state = PENDING; // 存儲成功或失敗的結果值 var value = null; // 存儲成功或失敗的處理程序,經過調用`.then`或者`.done`方法 var handlers = []; // 成功狀態變化 function fulfill(result) { state = FULFILLED; value = result; handlers.forEach(handle); 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