promise源码解析

javascript/jquery

浏览数:959

2019-1-10

源码参考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://segmentfault.com/a/1190000017833868