动手实现promise

一.接口分析

1.核心功能

  • resolve(value)/reject(err)

构造器参数resolve/reject虽然不是静态方法也不是实例方法,却是数据入口,所以是promise API的核心,promise机制的大部分秘密都在里面,所有公开接口都是在此基础上工作的

  • then(onFulfilled, onRejected)

resolve/reject外,then绝对是最重要的,其它所有实例方法/静态方法都不是必要的,而then却是必不可少的。换言之,能new Promise还支持then,整个promise机制就完成了,其它接口都只是锦上添花的东西,让promise变得更易用

2.实例方法

  • catch(onRejected)

等价于promise.then(null, onRejected),用户可以少写一点代码,同时赋予了语义(catch error)

3.静态方法

  • Promise.resolve(value)

提供一个wrapper,不确定value是不是Promise实例时,可以用该方法获取一个被包装好的Promise实例,内部机制比较复杂,详细信息请参考6.Promise.resolve(value) 解决

  • Promise.reject(reason)

同样提供一个wrapper,是创建否定Promise的快捷方式,内部机制比Promise.resolve(value)简单很多

  • Promise.all(iterable)

要么全都正常完成,要么因错误中断,未完成的全都停下来

  • Promise.race(iterable)

“赛跑”,不论结果是肯定还是否定,只要最快的

4.高级功能

  • 结果自动后抛

    // 错误后抛
    new Promise(function(resolve, reject) {reject('x')})
    .then(null)
    .then(null)
    .catch(function(err) {console.log(err)})
    // 值后抛
    new Promise(function(resolve, reject) {resolve('x')})
    .catch(null)
    .catch(null)
    .then(function(future) {console.log(future)})
    

    错误被抛给了最后的catch,只要没被“吃掉”就自动后抛,最后要么被“吃掉”,要么抛至顶层报错

  • 结果自动外抛

    // 外层then能够拿到内层future和error
    new Promise(function(resolve, reject) {
        resolve(new Promise(function(resolve, reject) {
            resolve(12);
            // reject(new Error('reject in nested Promise'));
        }).then(function(future) {
            return future + 1;
        }, function(error) {
            return new Error('reject again in nested Promise');
        }));
    }).then(onFulfilled, onRejected);
    

    外层的then能够拿到内层future和error,避免手动维护状态,也有利于错误集中处理

二.难点

难点在于promise机制本身是异步的,示例如下:

var p = new Promise(function(resolve, reject) {resolve(1)}).then(function(future) {return 2;});
// 立即输出p
console.log(p);
// 1s后输出p
setTimeout(function() {
    console.log(p);
}, 1000);

立即输出的结果是:

// console
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}

1s后输出的结果是:

// console
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 2}

在这种异步机制下,returnthrow都成了问题,例如:

// 异步
function nextTick(fn) {
    setTimeout(fn, 0);
}
// test
try {
    nextTick(function() {
        throw new Error('x');
    });
}
catch (err) {
    console.log('err: ' + err.message);
}

结果是无法catch错误,x被抛到了顶层,显示在控制台了。很容易想到把try..catch放进去,嗯,错误确实catch住了,可是要如何通知外层?

returnthrow都是传值的手段,在异步环境下无法直接使用,需要我们自行实现一种传值机制,这就是难点

P.S.有一篇文章中提到了try...catch的缺点,详细信息请查看前端代码异常监控

三.项目地址

地址:ayqy / myPromise – 代码托管 – 开源中国社区

注意:目前版本(v0.1.0 2015/12/13)存在很大问题,千万不要直接使用,因为目前内部机制是同步的

P.S.版本更新到可用时,笔者会在此处说明,在这之前,建议使用更靠谱的taylorhakes/promise-polyfill · GitHub,很轻巧的实现

参考资料

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code