Promise
的實現原理已經在 Promise 規範解讀及實現細節 (一) 中說的很清楚了,這裏將詳細分析 Promises/A+規範 中的Promise
解析過程,最後會實現一個 Promise
並提供用於測試的代碼segmentfault
promise.then(fn1).then(fn2).then(fn3)
這裏 promise.then()
的調用會產生一個新的promise(不一樣實例)promise
對於 then(fn)
上一級 promise
的終值會做爲 fn
的參數被傳入瀏覽器
對於 then(fn)
若是 then
返回一個 promise1
,fn
返回一個 promise2
那麼 promise1
的狀態和值由 promise2
決定異步
對於 then(fn)
若是 then
返回一個 promise1
,fn
返回一個 promise2
咱們想依賴於promise2
的調用會被添加到 promise1
中函數
promise2
決定 promise1
的結果若是promise2
有 then
方 var promise2Then = promise2.then
,咱們這樣promise2Then.bind(promise1,resolve,reject)
post
promise
的狀態和終值都是經過 當前 promise
的 resolve
和 reject
來改變的,因此只須要將 promise1
的這兩個函數經過promise2
的 then
方法添加的 promise2
的執行隊列中就能夠達到想要的效果測試
對於 promise.then(fn)
,then
方法返回 promise1
,fn
方法返回 x
,fn
接收到的參數爲 y
,這時須要對 x
和 y
分別運行Promise
解析過程this
x: [[Resolve]](promise1, x)
y: [[Resolve]](promise1, y)
x
的解析過程處理的是 回掉中返回 promise
的狀況y
的解析過程處理的是 向當前 promise
傳遞處理結果的那個 promise
的終值是 promise
的狀況url
因而可知 promise
的解析過程是遞歸的,遞歸的終點是 x
,y
不是promise
,在是對象或者函數的情形下不具有 then
方法code
(function(window) { var PENDING = 0; //PENDING 狀態 var RESOLVED = 1; //RESOLVED 狀態 var REJECTED = 2; //REJECTED 狀態 function IPromise(fn) { if (!(this instanceof IPromise)) return new IPromise(fn); //確保 經過 new IPromise() 和 IPromise() 都能正確建立對象 var state = PENDING; //promise 狀態 var value = null; //終值 var callback = []; //回掉函數隊列,在一種是兩個隊列,這裏是一個,因此存放的是對象 function reject(reason) {} //reject 方法 function resolve(result) {} //和(一)中的對比 這裏多了 執行 Promise 解析過程 的功能 function handle(handler) {} //添加或執行隊 callback 中的調用 /** *@param onFulfilled 經過 then 方法添加的 onFulfilled *@param onRejected 經過 then 方法添加的 onRejected * *@func 包裝用戶添加的回調 *由於這裏只有一個回掉隊列因此須要用 candy(糖果) 包裝成{onFulfilled:onFulfilled,onRejected:onRejected} * *@func 延遲調用handle *在 Promise 規範解讀及實現細節 (一) 中說 Promise(瀏覽器實現) 會被加入到microtask,因爲瀏覽器沒有提供除Promise *以外microtask的接口,因此 咱們要用 setTimeout 來延遲調用並添加到 macrotask * */ function candy(onFulfilled, onRejected) {} function getThen(value) {} //判斷 value 是否有 then 方法若是有則獲取 this.then = function(onFulfilled, onRejected) {} //暴露的 then 方法 doResolve(fn, resolve, reject); //執行 fn window.IPromise = IPromise; //將 IPromise 添加到瀏覽器的 window 上 } /** *Promise 解析過程 */ function doResolve(fn, resolvePromise, rejectPromise) {} //靜態私有方法,解析並執行promise(解析並執行fn和promise的處理結果) })(window);
以上經過 js
自執行函數將變量和函數限制在了做用域中,在全局的 window
上只暴露一個構造函數 IPromise
保證了全局不被污染
/** *@author ivenj *@date 2016-12-06 *@version 1.0 */ (function(window) { var PENDING = 0; var RESOLVED = 1; var REJECTED = 2; function IPromise(fn) { if (!(this instanceof IPromise)) return new IPromise(fn); var state = PENDING; var value = null; var callback = []; function reject(reason) { state = REJECTED; value = reason; callback.forEach(handle); callback = null; } /** * 這裏新增的內容是 知足Promise解析過程時 resolve和doResolve相互調用造成遞歸 **/ function resolve(result) { try { var then = getThen(result); if (then) { doResolve(then.bind(result), resolve, reject); //aa return; //這裏若是 resule 是有 then 方法則執行 doResolve 並返回不執行後續代碼 } //只有 result 不知足 解析過程時執行,即遞歸終點 state = RESOLVED; value = result; callback.forEach(handle); callback = null; } catch (e) { reject(e); } } function handle(handler) { if (state === PENDING) { callback.push(handler); } else { if (state === RESOLVED && typeof handler.onFulfilled === 'function') { handler.onFulfilled(value); } if (state === REJECTED && typeof handler.onRejected === 'function') { handler.onRejected(value); } } } function candy(onFulfilled, onRejected) { setTimeout(function() { handle({ onFulfilled: onFulfilled, onRejected: onRejected }); }, 0); } function getThen(value) { var type = typeof value; if (value && (type === 'object' || type === 'function')) { try{ var then = value.then; }catch(e){ reject(e); } if (typeof then === 'function') { return then; } } return null; } this.then = function(onFulfilled, onRejected) { var self = this; return new IPromise(function(resolve, reject) { candy(function(x) { if (typeof onFulfilled === 'function') { try { resolve(onFulfilled(x)); //cc 運行 [[Resolve]](promise, x) } catch (e) { reject(e); } } else { resolve(x); } }, function(error) { if (typeof onRejected === 'function') { try { resolve(onRejected(error)); } catch (e) { reject(e); } } else { reject(error); } }); }); }; doResolve(fn, resolve, reject); } /** *Promise 解析過程 */ function doResolve(fn, resolvePromise, rejectPromise) { var done = false; //用於保證只調用一次 try { fn(function(y) { if (done) return; done = true; resolvePromise(y); //bb 若是 resolvePromise 以值 y 爲參數被調用,則運行 [[Resolve]](promise, y) }, function(reason) { if (done) return; done = true; rejectPromise(reason); }); } catch (e) { if (done) return; done = true; rejectPromise(e); } } window.IPromise = IPromise; })(window);
這裏是用於測試的代碼 讀者將以上代碼和如下代碼粘貼到瀏覽器去運行 一秒後會打印 {url: "http://ivenj_", value: 10}
function post(url, callback) { setTimeout(function() { var data = { //模擬異步處理結果 url:url, value:10 }; callback(data); }, 1000); } var promise = IPromise(function(resolve, reject){ post('http://ivenj_', function(data){ resolve(data); }); }); promise.then(function(data){ console.log(data); });
Promise
實現最核心的內容是代碼中的 //aa
//bb
//cc
讀者須要着重體會這三處
Promise 到此已經結束