Promise 規範解讀及實現細節 (二)

開始前

Promise的實現原理已經在 Promise 規範解讀及實現細節 (一) 中說的很清楚了,這裏將詳細分析 Promises/A+規範 中的Promise解析過程,最後會實現一個 Promise 並提供用於測試的代碼segmentfault

then 方法分析

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 的結果

若是promise2thenvar promise2Then = promise2.then,咱們這樣promise2Then.bind(promise1,resolve,reject)post

promise 的狀態和終值都是經過 當前 promiseresolvereject 來改變的,因此只須要將 promise1 的這兩個函數經過promise2then 方法添加的 promise2 的執行隊列中就能夠達到想要的效果測試

Promise 解析過程

對於 promise.then(fn),then 方法返回 promise1fn 方法返回 x,fn 接收到的參數爲 y,這時須要對 xy 分別運行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 到此已經結束

相關文章
相關標籤/搜索