一.接口分析
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}
在这种异步机制下,return
和throw
都成了问题,例如:
// 异步
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
住了,可是要如何通知外层?
return
和throw
都是传值的手段,在异步环境下无法直接使用,需要我们自行实现一种传值机制,这就是难点
P.S.有一篇文章中提到了try...catch
的缺点,详细信息请查看前端代码异常监控
三.项目地址
地址:ayqy / myPromise – 代码托管 – 开源中国社区
注意:目前版本(v0.1.0 2015/12/13)存在很大问题,千万不要直接使用,因为目前内部机制是同步的
P.S.版本更新到可用时,笔者会在此处说明,在这之前,建议使用更靠谱的taylorhakes/promise-polyfill · GitHub,很轻巧的实现
参考资料
- [翻译] We have a problem with promises:弄明白了promise的执行机制就不会出现这些问题,四种promise的区别也没什么好说的,但文中提到的错误值得注意