Promise 规范解读及实现细节 (二)

开始前

Promise的实现原理已经在 Promise 规范解读及实现细节 (一) 中说的很清楚了,这里将详细分析 Promises/A+规范 中的Promise解析过程,最后会实现一个 Promise 并提供用于测试的代码

then 方法分析

promise.then(fn1).then(fn2).then(fn3) 这里 promise.then() 的调用会产生一个新的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)

promise 的状态和终值都是通过 当前 promiseresolvereject 来改变的,所以只需要将 promise1 的这两个函数通过promise2then 方法添加的 promise2 的执行队列中就可以达到想要的效果

Promise 解析过程

对于 promise.then(fn),then 方法返回 promise1fn 方法返回 x,fn 接收到的参数为 y,这时需要对 xy 分别运行Promise解析过程

x: [[Resolve]](promise1, x)
y: [[Resolve]](promise1, y)
x 的解析过程处理的是 回掉中返回 promise 的情况
y 的解析过程处理的是 向当前 promise 传递处理结果的那个 promise 的终值是 promise 的情况

由此可见 promise 的解析过程是递归的,递归的终点是 x,y 不是promise,在是对象或者函数的情形下不具备 then 方法

代码结构

(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 到此已经结束

    原文作者:_ivenj
    原文地址: https://segmentfault.com/a/1190000010234247
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞