资讯专栏INFORMATION COLUMN

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

Michael_Lin / 3321人阅读

摘要:开始前的实现原理已经在规范解读及实现细节一中说的很清楚了,这里将详细分析规范中的解析过程,最后会实现一个并提供用于测试的代码方法分析这里的调用会产生一个新的不同实例对于上一级的终值会作为的参数被传入对于如果返回一个返回一个那么的状态和值由决

开始前

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/88328.html

相关文章

  • Promise 规范解读实现细节 (一)

    摘要:宏任务和微任务这两个是指两个队列,脚本整体代码的回调及渲染都会被加入到队列中回调浏览器实现回调都会被加入到队列。 1. macrotask (宏任务)和 microtask (微任务) 这两个是指两个队列,脚本整体代码、setTimeout、setInterval、setImmediate、I/O、的回调及UI渲染都会被加入到 macrotask 队列中, process.nextTi...

    gougoujiang 评论0 收藏0
  • 理解 Promise 的工作原理

    摘要:前两个函数对应的两种状态和的回调函数。返回值是和对应的方法,但是会在下一事件循环返回。此外,在规范中,由方法生成的对象是已执行还是已拒绝,取决于由方法调用的那个回调是返回值还是抛出错误。但是对于其工作原理却有些懵懂和好奇。 原文: https://blog.coding.net/blog/how-do-promises-work Javascript 采用回调函数(callback)来...

    Achilles 评论0 收藏0
  • Promises A+规范原文解读 + es6实现(附详细注释)

    摘要:英文官方文档原文前言写本文的目的,是为了更好的理解,通过解读翻译原文,逐行解析原文通过代码一行一行实现。英中原因是一个值结果表明被拒绝的原因。英中在法律允许的范围内,组织已放弃所有版权及规范的相关或相邻权利。 英文官方文档原文:https://promisesaplus.com/ 前言 写本文的目的,是为了更好的理解promise,通过解读翻译原文,逐行解析原文通过代码一行一行实现。...

    v1 评论0 收藏0
  • 超详细的webpack原理解读

    摘要:生成文件,是模块构建的终点,包括输出文件与输出路径。这里配置了处理各模块的,包括预处理,编译,图片处理。各插件对象,在的事件流中执行对应的方法。修改改成引入模块在目录下执行, webpack原理解读 本文抄自《深入浅出webpack》,建议想学习原理的手打一遍,操作一遍,给别人讲一遍,然后就会了在阅读前希望您已有webpack相关的实践经验,不然读了也读不懂 本文阅读需要几分钟,理解需...

    wenhai.he 评论0 收藏0
  • 一步一步实现一个符合PromiseA+规范Promise库(2)

    摘要:我们都知道,方法中有和两个回调函数,所以我们要处理一下这两个回调函数。我们实现了异步调用,在方法中返回或者值,实现了方法中可以没有回调函数也能把执行结果传入下一次的方法中。 Hello everybody。我又来啦,还记得我们上一张实现的内容吗? showImg(https://segmentfault.com/img/bV6UaU?w=102&h=95); 上一张我们实现了一个简单的...

    jsliang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<