资讯专栏INFORMATION COLUMN

promise源码解析

kamushin233 / 625人阅读

摘要:源码参考主要内容的迭代设计中主要的代码片段,翻译一部分加上自己的理解,同时指出的一些特性。先贴完整代码安全性和稳定性保证和在未来他们被调用的时候,应该是和注册时的顺序是保持一致的。这将显著降低异步编程中流程控制出错可能性。

源码参考https://github.com/kriskowal/...

主要内容:promise的迭代设计中主要的代码片段,翻译一部分加上自己的理解,同时指出promise的一些特性。

promise的雏形
var maybeOneOneSecondLater = function () {
    var callback;
    setTimeout(function () {
        callback(1);
    }, 1000);
    return {
        then: function (_callback) {
            callback = _callback;
        }
    };
};

maybeOneOneSecondLater().then(callback1);

promise诞生,比起传统回调,它返回了一个表示最终结果的对象(一个承诺),我们可以在这个对象上调用函数观察它的实现或者是拒绝。
但本质上还是传入回调,任人宰割。
好的是,promise在异步操作和回调之间提供了一个中间层,在这个中间层里可以搞一些事情,解决传统回调的蛋疼问题。
改进后的promise可以让我们的回调不’任人宰割‘了。

第一次改进

很明显目前这个promise只能处理一个回调,传入多个回调会覆盖,所以数据结构上应该用一个数组来储存回调函数,需要执行的时候,依次执行。

let defer = () => {
    let pending = [],value;
    return {
        resolve(_value){
            value = _value
            for(let i = 0;i < pending.length; i++){
                pending[i](value)
            }
            pending = undefined;
        },
        then(_callback){
            if(pending){
                pending.push(_callback)
            }else{
                _callback();
            }
        }
    }
}
let oneOneSecondLater = () => {
    let result = defer();
    setTimeout(()=> {
        result.resolve(1);
    }, 1000);
    return result;
};

oneOneSecondLater().then(callback);

这里对代码做一些解释:

defer函数的作用:产生promise对象和resolve函数,可以理解为构造函数;

resolve函数是你在封装异步函数时用的,promise是你使用异步函数时用的,defer函数像是一个中介,给两头服务;

oneOneSecondLater函数封装了我们的异步操作setTimeout;

result.resolve():异步操作完成后告诉promise,promise会替你执行回调,这是它作为一个中介应该做的;

oneOneSecondLater().then(callback):oneOneSecondLater函数的使用者需要告诉promise,成功后执行什么;

好了,目前我们的promise可以接受多次回调,并在异步操作完成后顺序执行了。

第二次改进

promise这个中介规定,异步操作只能成功一次(resove只能调用一次哟)。
也就是说,使用promise封装异步操作的同事们不可能让你的回调执行两次了。。你就大胆的传进去吧。。

let defer = () => {
    let pending = [],value
    return {
        resolve(_value){
            if(pending){
                value = _value
                for(let i = 0;i < pending.length; i++){
                    pending[i](value)
                }
                pending = undefined;
            }else{
                throw new Error("A promise can only be resolved once.")
            }
        },
        then(_callback){
            if(pending){
                pending.push(_callback)
            }else{
                _callback();
            }
        }
    }
}

补充一点:

因为promise在异步操作成功后,就将pending设为了undefined,这也说明,promise向我们保证了:异步状态一旦改变,就定格了。

所以如果一个异步操作已经成功,你再传回调进去,那就会直接执行:if(pending){pending.push(_callback)}else{_callback();}

第三次改进:职责分离
let defer = () => {
    let pending = [],value;
    return {
        resolve(_value){
            if(pending){
                value = _value
                for(let i = 0;i < pending.length; i++){
                    pending[i](value)
                }
                pending = undefined;
            }else{
                throw new Error("A promise can only be resolved once.")
            }
        },
        promise: {
            then (callback) {
                if (pending) {
                    pending.push(callback);
                } else {
                    callback(value);
                }
            }
        }
    }
}

这个改进就很小了,只是把then封装到promise对象中,让resolve和promise两个对象各司其职;

resolve是在封装异步操作的时候用的,promise是在使用异步操作时候用的;

第四次改进:加链式操作

熟悉promise的同学应该知道,每次then执行完成后都是会默认返回promis的,就是为了方便链式操作。
先贴上完整代码:

var ref = function (value) {
    if (value && typeof value.then === "function")
        return value;
    return {
        then: function (callback) {
            return ref(callback(value));
        }
    };
};
var defer = function () {
    var pending = [], value;
    return {
        resolve: function (_value) {
            if (pending) {
                value = ref(_value); // values wrapped in a promise
                for (var i = 0, ii = pending.length; i < ii; i++) {
                    var callback = pending[i];
                    value.then(callback); // then called instead
                }
                pending = undefined;
            }
        },
        promise: {
            then: function (_callback) {
                var result = defer();
                // callback is wrapped so that its return
                // value is captured and used to resolve the promise
                // that "then" returns
                var callback = function (value) {
                    result.resolve(_callback(value));
                };
                if (pending) {
                    pending.push(callback);
                } else {
                    value.then(callback);
                }
                return result.promise;
            }
        }
    };
};

直接看是有点懵逼的。。
分开来看一下,先看这一段promise对象:

promise: {
    then: function (_callback) {
        var result = defer();
        // callback is wrapped so that its return
        // value is captured and used to resolve the promise
        // that "then" returns
        var callback = function (value) {
            result.resolve(_callback(value));
        };
        if (pending) {
            pending.push(callback);
        } else {
            value.then(callback);
        }
        return result.promise;
    }
}

链式操作的形式:xxx.then(callback1).then(callback2)

也就是说我们的then函数返回的是promise对象;

所以在then函数的开始, var result = defer();创建一个空promise对象。最后return result;

callback1在执行后要把结果给callback2吧,怎么给呢?

先执行回调:callback1(value)

执行完了通知下一个promise可以执行了:result.resolve()

合体:result.resolve(callback1(value))

再看这一段resolve函数:

 resolve: function (_value) {
    if (pending) {
        value = ref(_value); // values wrapped in a promise
        for (var i = 0, ii = pending.length; i < ii; i++) {
            var callback = pending[i];
            value.then(callback); // then called instead
        }
        pending = undefined;
    }
},

resolve(_value)中的_value是封装者给回调的参数;

_value可能是一个promise吗?当然可以;

ref函数就是为了判断_vlaue是不是promise,如果是则原样返回,不是的话包装成一个promise返回,方便我们统一处理;

经过ref处理过的value肯定是一个promise了,所以我们统一写成:value.then(callback)

贴一下ref:

var ref = function (value) {
    if (value && typeof value.then === "function")
        return value;
    return {
        then: function (callback) {
            return ref(callback(value));
        }
    };
};

这里看到,如果vlaue是promise,就直接返回;

value如果不是promise,包装成promise,把value传入callback;

所以ref只是作者抽出去的一个工具函数哈,其实不抽的话更容易看懂= =!
把ref合进resolve,大家看看是不是容易理解了:

resolve: function (_value) {
    if (pending) {
        if(_value && typeof _value.then === "function") {
            for (var i = 0, ii = pending.length; i < ii; i++) {
                var callback = pending[i];
                _value.then(callback); 
            }
        }else {
            for (var i = 0, ii = pending.length; i < ii; i++) {
                pending[i](_value);
            }
        }
        pending = undefined;
    }
},

这种写法没有保存_value到value中,仅仅是为了解释resolve的一段代码

增加错误处理

目前来看的话,promise只接受了一个回调,很明显这里需要再接受一个错误回调,根据异步操作的执行结果,选择执行哪个。
先贴完整代码:

var defer = function () {
    var pending = [], value;
    return {
        resolve: function (_value) {
            if (pending) {
                value = ref(_value);
                for (var i = 0, ii = pending.length; i < ii; i++) {
                    value.then.apply(value, pending[i]);
                }
                pending = undefined;
            }
        },
        promise: {
            then: function (_callback, _errback) {
                var result = defer();
                // provide default callbacks and errbacks
                _callback = _callback || function (value) {
                    // by default, forward fulfillment
                    return value;
                };
                _errback = _errback || function (reason) {
                    // by default, forward rejection
                    return reject(reason);
                };
                var callback = function (value) {
                    result.resolve(_callback(value));
                };
                var errback = function (reason) {
                    result.resolve(_errback(reason));
                };
                if (pending) {
                    pending.push([callback, errback]);
                } else {
                    value.then(callback, errback);
                }
                return result.promise;
            }
        }
    };
};
let ref = (value) => {
    if (value && typeof value.then === "function")
        return value;
    return {
        then: function (callback) {
            return ref(callback(value));
        }
    };
};

let reject = (reason) => {
    return {
        then: function (callback, errback) {
            return ref(errback(reason));
        }
    };
};
安全性和稳定性

保证callbacks和errbacks在未来他们被调用的时候,应该是和注册时的顺序是保持一致的。这将显著降低异步编程中流程控制出错可能性。

let enqueue = (callback) => {
    setTimeout(callback,1)
}

 resolve: function (_value) {
    if (pending) {
        value = ref(_value);
        for (let i = 0, ii = pending.length; i < ii; i++) {
            enqueue(function () {
                value.then.apply(value, pending[i]);
            });
        }
        pending = undefined;
    }
}
        
let ref = function (value) {
    if (value && value.then)
        return value;
    return {
        then: function (callback) {
            let result = defer();
            // XXX
            enqueue(function () {
                result.resolve(callback(value));
            });
            return result.promise;
        }
    };
};

let reject = function (reason) {
    return {
        then: function (callback, errback) {
            var result = defer();
            // XXX
            enqueue(function () {
                result.resolve(errback(reason));
            });
            return result.promise;
        }
    };
};

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

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

相关文章

  • Promise学习笔记(四):源码core.js解析(下)

    摘要:源码阅读阶段紧接上一篇这次我们开始我们最常用到的部分的源码解析传入参数为两个函数和判断调用者是否为对象跳转到了一个叫做的函数里面新建一个对象传入函数传入给和一个新的对象返回新的对象在这里我们先看看在调用者不是对象时到底做了什么比想象的要简单 源码阅读阶段 紧接上一篇,这次我们开始Promise我们最常用到的then部分的源码解析. then() //传入参数为两个函数,onFulfil...

    VincentFF 评论0 收藏0
  • Promisify 的源码解析

    摘要:参考文档升级后的函数回调参数问题中的使用方法和还是不一样的源码讲解的内部机制优化相关内容文章官方文档简述使用过的都知道这个方法的作用,通过该方法会让形式的函数风格转换成方法,可以认为是一颗语法糖,例如接下来我们就分析一下这个的内部流程。 参考文档 升级bluebird 3后Promise.promisify的函数回调参数问题:3中的使用方法和2还是不一样的 How does Bl...

    gougoujiang 评论0 收藏0
  • Promise学习笔记(三):源码core.js解析(上)

    摘要:源码阅读阶段先理解根本吧想快点理解的话可以直接跳到下个标题这部分根据理解将持续修改空函数用于判断传入构造器的函数是否为空函数如果为空函数构造一个对象并初始化状态为终值回调状态和队列记录内部最后的一次错误空对象标识表示发生了错误暴露模块接口为 源码阅读阶段 先理解Promise根本吧,想快点理解的话可以直接跳到下个标题.这部分根据理解将持续修改. Promise(fn) function...

    wuyangchun 评论0 收藏0
  • jQuery源码解析Deferred异步对象

    摘要:回调队列对象,用于构建易于操作的回调函数集合,在操作完成后进行执行。对象对象,用于管理回调函数的多用途列表。如果传入一个延迟对象,则返回该对象的对象,可以继续绑定其余回调,在执行结束状态之后也同时调用其回调函数。 在工作中我们可能会把jQuery选择做自己项目的基础库,因为其提供了简便的DOM选择器以及封装了很多实用的方法,比如$.ajax(),它使得我们不用操作xhr和xdr对象,直...

    Coding01 评论0 收藏0
  • vue 源码解析 --虚拟Dom-render

    摘要:用于延迟执行一段代码,它接受个参数回调函数和执行回调函数的上下文环境,如果没有提供回调函数,那么将返回对象。 instance/index.js function Vue (options) { if (process.env.NODE_ENV !== production && !(this instanceof Vue) ) { warn(Vue is a ...

    Tony 评论0 收藏0

发表评论

0条评论

kamushin233

|高级讲师

TA的文章

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