前言
Promise 对象,相信学前端的小伙伴们都对它很熟了,它是一种现在前端异步编程的主流形式,关于它的文章从基础使用到底层原理分析、实现网上是一抓一大把,它也是现在前端面试的高频题目,深度掌握它也成为一个衡量前端能力的标准了。
之前也写过一篇关于 Promise 对象的文章(一文,一定能让你手动实现Promise),不过那会写得比较简单,这次算是来给它补全(来还债了(ー_ー)!!),废话不多说,下面马上就来开始本文的愉快之旅。
这次所写的 Promise 对象是以 Promises/A+ (中文规范)为标准的,ES6 中就是采用了 Promise/A+ 规范。
Promise的规范有很多,如Promise/A,Promise/B,Promise/D以及Promise/A的升级版Promise/A+。Promise/A+并未规范catch、all、race等这些方法,这些方法都是ES6自己规范的,其实也不难,后面也会一一讲解到。
源码实现
本章尝试以例子的形式来写出整个 Promise 对象的源码,每行代码会备注详细的注解不怕看不懂哦。
基础结构
- 我们以下面基本使用的例子先来引出
Promise对象的整体结构。
let p = new Promise((resolve, reject) => {
console.log('Start')
resolve('橙某人');
})
p.then(r1 => {
console.log(r1)
}, e1 => {
console.log(e1)
})
console.log('End');
通过上面 Promise 对象的基本使用,我们能先定义出它的构造函数、.then(onFulfilled, onRejected) 方法以及还有两个回调函数 resolve() 和 reject() 的定义,当然,它们是形参,名字你可以随便定义。
// 定义构造函数
function myPromise(executor) {
let _this = this; // 保留 this 的指向,防止下面使用过程中 this 指向不明
_this.status = 'pending'; // status有三种状态: pending || fulfilled || rejected
_this.result = undefined; // 保存调用 resolve(result) 方法传递的值
_this.reason = undefined; // 保存调用 reject(reason) 方法传递的值
// 定义 resolve()
function resolve(result) {
if(_this.status === 'pending') {
_this.result = result; // 保存成功的值
_this.status = 'fulfilled'; // 把 Promise 对象的状态改为成功
}
}
// 定义 reject()
function reject(reason) {
if(_this.status === 'pending') {
_this.status = 'rejected'; // 保存失败的值
_this.reason = reason; // 把 Promise 对象的状态改为失败
}
}
// 立即执行 executor(), 执行 executor() 过程可能会出错, 所以要严谨进行try/catch一下, 并且错误结果能给 Promise 捕获
try{
executor(resolve, reject);
}catch(err) {
reject(err)
}
}
// 定义 .then() 方法, 它的参数是可选的, 只要不是函数就忽略即可
myPromise.prototype.then = function (onFulfilled, onRejected) {
if(this.status === 'fulfilled' && typeof onFulfilled === 'function') {
// .then() 调用是一个异步过程
setTimeout(() => {
onFulfilled(this.result);
})
}
if(this.status === 'rejected' && typeof onRejected === 'function') {
setTimeout(() => {
onRejected(this.reason);
})
}
}
一个 Promise 会有三种状态,分别是 pending/fulfilled/rejected ,可以简单理解为等待中、成功和失败三种状态,这是基本知识了,就不作过多介绍了。整体过程不是很难,唯一需要注意的是 .then() 方法是一个异步方法,所以我们把它们放在 setTimeout 中去执行。
- 下面我们来调整一下上面的例子:
let p = new Promise((resolve, reject) => {
console.log('Start')
setTimeout(() => {
resolve('橙某人');
})
})
p.then(r1 => {
console.log(r1)
}, e1 => {
console.log(e1)
})
console.log('End');
我们异步调用了 resolve('橙某人'); 但它的运行结果和上面的一致,来看看如何实现这个异步调用过程。
function myPromise(executor) {
...
_this.onFulfilledCbs = []; // 调用 then(onFulfilled, onRejected) 方法时, 如果 status 为 pending 状态, 说明 resolve() 为异步调用, 所以 Promise 状态还没改变, 则先将 onFulfilled 回调函数存储起来
_this.onRejectedCbs = []; // 调用 then(onFulfilled, onRejected) 方法时, 如果 status 为 pending 状态, 说明 reject() 为异步调用, 所以 Promise 状态还没改变, 则先将 onRejected 回调函数存储起来
function resolve(result) {
if(_this.status === 'pending') {
_this.result = result;
_this.status = 'fulfilled';
// 执行所有 then(onFulfilled, onRejected) 方法的 onFulfilled
while (_this.onFulfilledCbs.length) {
_this.onFulfilledCbs.shift()(); // 按 then() 方法的调用顺序执行并删除
}
}
}
function reject(reason) {
if(_this.status === 'pending') {
_this.reason = reason;
_this.status = 'rejected';
// 执行所有 then(onFulfilled, onRejected) 方法的 onRejected
while (_this.onRejectedCbs.length) {
_this.onRejectedCbs.shift()(); // 按 then() 方法的调用顺序执行并删除
}
}
}
...
}
myPromise.prototype.then = function (onFulfilled, onRejected) {
...
// Promise 对象还是 pending 状态, 则说明 resolve() 或者 reject() 进行了异步调用
if(this.status === 'pending') {
if(typeof onFulfilled === 'function') {
/**
* 之所以 push 一个函数, 主要是为了回调函数能传值.
* 也可以直接 push 函数, 传值交由上面来完成:
* this.onFulfilledCbs.push(onFulfilled);
* _this.onFulfilledCbs.shift()(_this.result)
*/
this.onFulfilledCbs.push(() => {
onFulfilled(this.result);
})
}
if(typeof onRejected === 'function') {
this.onRejectedCbs.push(() => {
onRejected(this.reason);
})
}
}
}
其实异步调用 resolve() 或者 reject() 则 .then() 方法会比它们俩先执行,那么 .then() 内部使用的 Promise 对象的状态会是 pending 等待状态,那么我们只能先把 .then(onFulfilled, onRejected) 方法的成功回调和失败回调先存起来,只有当真正调用过 resolve() 或者 reject() 再去把它们拿出来执行即可。
then() 方法
Promise 对象的基本结构不是很难,也比较好理解,它最强大的地方是它支持链式调用,这是最复杂也是最重点的内容,接下来我们来研究研究它的链式调用,上重头戏了。
链式调用
我们一样先看例子:
let p = new Promise((resolve, reject) => {
console.log('Start')
setTimeout(() => {
resolve('橙某人');
})
})
p.then(r1 => {
return r1;
}).then(r2 => {
return r2;
}).then(r3 => {
console.log(r3); // 橙某人
})
console.log('End');
由例子上看,我们可以知道每次调用完 then() 方法必然会返回一个 Promise 对象,它才能接下来继续链式调用下去。而且, then() 中能使用 return 把结果继续往后传递下去,基于这两点我们再来改造下 then() 方法。
myPromise.prototype.then = function (onFulfilled, onRejected) {
// 返回一个新的 Promise 对象
return new myPromise((resolve, reject) => {
if(this.status === 'fulfilled' && typeof onFulfilled === 'function') {
setTimeout(() => {
// 执行 onFulfilled or onRejected 方法可能会出现错误, 所以try/catch一下,并把错误往下传递
try{
// 获取到 return 的值
let x = onFulfilled(this.result);
// 调用新的 Promise 对象的 resolve() 把值继续传下去
resolve(x);
}catch(e) {
reject(e);
}
})
}
if(this.status === 'rejected' && typeof onRejected === 'function') {
setTimeout(() => {
try{
let x = onRejected(this.reason);
reject(x);
}catch(e) {
reject(e);
}
})
}
if(this.status === 'pending') {
if(typeof onFulfilled === 'function') {
this.onFulfilledCbs.push(() => {
try{
let x = onFulfilled(this.result);
resolve(x);
}catch(e) {
reject(e);
}
})
}
if(typeof onRejected === 'function') {
this.onRejectedCbs.push(() => {
try{
let x = onRejected(this.reason);
reject(x);
}catch(e) {
reject(e);
}
})
}
}
})
}
通过在 then() 中返回一个新的 Promise 对象,就能实现无限链式调用下去了,是不是挺简单;而通过执行 onFulfilled() 或者 onRejected() 方法来获取return 的值,也就是上面代码中的 x ;最后我们通过新的 Promise 对象的 resolve(x) 和 reject(x) 方法就能把值继续往下传啦(-^〇^-)。当然执行 onFulfilled() 或者 onRejected() 可能会出现错误,所以我们都加上了 try/catch 来捕获一下错误。
值穿透
然后,你以为这就完了吗?还没呢,我们再来看个例子:
let p = new myPromise((resolve, reject) => {
console.log('Start')
setTimeout(() => {
resolve('橙某人');
})
})
p
.then()
.then()
.then()
.then(r4 => {
console.log(r4); // 橙某人
})
console.log('End');
是不是这种“值穿透”传递也挺神奇的?我们来看看它要如何去实现。
myPromise.prototype.then = function (onFulfilled, onRejected) {
// 解决值穿透
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : result => result;
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}; // onRejected 直接用 throw 抛出错误,这样它会被 try/catch 捕获到; 注意 onRejected 中的错误不能以 return 的形式传递,都是通过 throw 往后抛下去
// 返回一个新的 Promise 对象
...
}
其实这个过程就是一个初始化默认值过程,也就是 .then() 会变成 .then(result => result),前面我们说过对于 then() 方法的两个参数 onFulfilled 和 onRejected 我们只考虑它们是函数的情况。现在,当它们不是函数的时候,我们就给它们默认值一个函数并且把结果继续往后抛,就能实现所谓的值穿透了。
值类型判断
接下来就是 then() 方法的最后一步了,这一步也挺复杂的,需要对 then(onFulfilled, onRejected) 两个参数 return 的值进行验证,也就是对 x 做一些类型检验。按照 Promises/A+ 标准规范的信息,我们需要对 x 做这以下几个校验:
呃......是不是都看懵逼了,这是图灵社区译的中文文档,应该算是很贴近了,你也可以去看看英语文档,其实应该也差不多,一般官方文档的话语都是那么难以理解的。不过,不用着急,我把测试例子写出来,你可能就好理解很多了。
x与then()新返回的Promise相等。
let p = new Promise((resolve, reject) => {
console.log('Start')
resolve('橙某人');
})
let t = p.then(() => {
return t
})
console.log('End');
x为另一个Promise对象。 新的Promise对象状态处于fulfilled或者rejected状态。
let p = new Promise((resolve, reject) => {
console.log('Start')
resolve('橙某人');
});
let pNew = new Promise((resolve, reject) => {
resolve('yd');
// reject('yd');
});
p.then(() => {
return pNew
}).then(r => {
console.log(r);
});
console.log('End');
新的 Promise 对象状态处于 pending 状态。
let p = new Promise((resolve, reject) => {
console.log('Start')
resolve('橙某人');
})
let pNew = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('yd'); // 1秒后才调用, 让 Promise 处于 pending 状态
}, 1000)
});
p.then(() => {
return pNew
}).then(r => {
console.log(r); // 一秒后才输出
});
console.log('End');
第二个测试的两个小例子,结果虽然一样,但第一个的结果是控制台立即就输出了 yd,但第二个的结果是控制台等了一秒后才输出 yd。
- 如果 x 为对象或者函数。
let p = new Promise((resolve, reject) => {
console.log('Start')
resolve('橙某人');
})
// x 为对象
p.then(() => {
return {
then: function(resolve, reject) {
console.log('我是对象的then属性', this);
resolve('yd')
}
}
}).then(r => {
console.log(r); // yd
}, e => {
console.log(e)
})
// x 为函数
function fn() {}
fn.then = function(resolve, reject) {
console.log('我是对函数的then属性', this);
resolve('yd')
}
p.then(() => {
return fn;
}).then(r => {
console.log(r); // yd
})
console.log('End');
这样子是不是有点像是把对象和函数的 then 属性造了一个新的 Promise 对象呢?这也告诉我们取名字的时候要注意哦,千万不要和关键字有关联(^ω^)。
下面我们先来看看 then() 方法的调整:
myPromise.prototype.then = function (onFulfilled, onRejected) {
...
var p = new myPromise((resolve, reject) => {
if(this.status === 'fulfilled' && typeof onFulfilled === 'function') {
setTimeout(() => {
let x = onFulfilled(this.result);
// 借助 resolvePromise() 统一验证 x
resolvePromise(p, x, resolve, reject);
})
}
if(this.status === 'rejected' && typeof onRejected === 'function') {
setTimeout(() => {
let x = onRejected(this.reason);
resolvePromise(p, x, resolve, reject);
})
}
if(this.status === 'pending') {
if(typeof onFulfilled === 'function') {
this.onFulfilledCbs.push(() => {
let x = onFulfilled(this.result);
resolvePromise(p, x, resolve, reject);
})
}
if(typeof onRejected === 'function') {
this.onRejectedCbs.push(() => {
let x = onRejected(this.reason);
resolvePromise(p, x, resolve, reject);
})
}
}
})
return p;
}
因为对 x 验证的部分比较复杂和重复,所以另外写一个 resolvePromise() 方法。
/**
* 验证 x 的类型
* @param {*} p: 新返回的 Promise 对象
* @param {*} x: onFulfilled or onRejected 返回的值
* @param {*} resolve: 新 Promise 对象的 resolve 方法
* @param {*} reject: 新 Promise 对象的 reject 方法
* @returns
*/
function resolvePromise(p, x, resolve, reject) {
// 1. 如果 x 为新返回的 Promise 对象, 则直接抛出错误
if (x != undefined && p === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}
// 2. 如果 x 为另一个 myPromise 对象, 也就是显式 return new myPromise()
if (x instanceof myPromise) {
if (x.status === 'pending') { // 如果 x 这个 myPromise 对象处于 pending 状态, 则要等待直 x 被执行或拒绝
x.then(r => {
resolvePromise(p, r, resolve, reject); // 递归等待 x 被执行
}, e => {
reject(e);
})
} else { // 如果 x 这个 myPromise 对象已经处于 fulfilled or rejected 状态, 则直接再次调用 then() 方法!
x.then(resolve, reject); // 这里的意思其实可以理解为, 我们执行了新的 myPromise 对象的 then() 方法, 它的两个回调参数, 就是我们下一个 then() 两个回调的参数.
}
return;
}
// 3. 如果 x 为对象或者函数, 如果不是就是普通的数据类型, 直接通过
var called; // 只调用一次, 防止重复调用
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 如果 x.then 为一个函数, 则我们会执行这个函数, 并且函数内部的 this 指向 x, 执行这个函数内部可能会有错误, 所以需要 try/catch 一下
try {
let then = x.then; // 获取 x 身上的 then() 方法
if (typeof then === 'function') {
then.call(x, function (res) {
if (called) return;
called = true;
// 可能 res 还是一个 promise 对象, 所以需要递归下去判断 res(也就是x) 的类型
resolvePromise(p, res, resolve, reject); // 递归
}, function (err) {
if (called) return;
called = true;
reject(err)
})
} else {
resolve(x); // 普通值, x.then 可能是 x.then = '橙某人
}
} catch (e) {
reject(e)
}
} else {
resolve(x); // 普通值
// 这里为什么直接就调用 resolve() 不考虑 reject(), 因为规定错误不能通过 return 来传递, 只能通过 throw 的形式, 也就是 onRejected 中 return 是无效的
}
}
resolvePromise() 方法是比较复杂的,从上面文字的介绍你就应该能感觉到,它里面涉及到了递归的概念,我觉得看懂它的最好方式就是跟着例子慢慢去分析,加油骚年,有什么疑问也欢迎你留言给我,必回。
静态方法
熬过最难的一部分,剩下会简单一些了,我们继续往下瞧。
catch
catch 方法比较常用了,主要用于捕获各种异常,它也支持链式调用,我们先来瞧瞧这些例子。
// 例子一
let p = new Promise((resolve, reject) => {
resolve('橙某人');
})
p
.then()
.catch()
.then(r => {
console.log(r); // 橙某人
})
// 例子二
let p = new Promise((resolve, reject) => {
reject('出错');
})
p
.then(()=>{}, e => {
console.log(e); // 出错
})
// 例子三
let p = new Promise((resolve, reject) => {
reject('出错');
})
p
.then()
.catch(e => {
console.log(e); // 出错
})
// 例子四
let p = new Promise((resolve, reject) => {
resolve('橙某人');
})
p
.then(() => {
console.log(a)
}, e => {
console.log(e); // ReferenceError: a is not defined
})
// 例子五
let p = new Promise((resolve, reject) => {
resolve('橙某人');
})
p
.then(() => {
console.log(a)
})
.catch(e => {
console.log(e); // ReferenceError: a is not defined
})
由上面的例子我们也知道 catch 方法会返回一个 Promise 对象来完成链式调用,而且它的功能和 then(onFulfilled, onRejected) 方法中的 onRejected 基本是一致的。我们来瞧瞧它的源码样子如何:
myPromise.prototype.catch = function(onRejected) {
// 相当于再次调用了一次 then() 方法, 把传进来的参数作为了 then() 方法的第二个参数
return this.then(null, onRejected);
}
resolve
Promise.resolve() 方法是 Promise 对象的静态方法,可不要把它也挂载到原型上去了哦,它的作用是返回一个 fulfilled 状态的新 Promise 对象。
// 例子一
let p1 = Promise.resolve('橙某人1');
p1.then(r1 => {
console.log(r1); // 橙某人1
})
// 等同
let p11 = new Promise((resolve, reject) => {
resolve('橙某人1');
});
// 例子二
let p2 = Promise.resolve(p1);
console.log(p2 === p1); // true
p2.then(r2 => {
console.log(r2); // 橙某人1
})
看例子一,你会发现 Promise.resolve() 像是一个简写形式,看例子二,当我们传递的值为一个 Promise 对象的时候,就会返回这个 Promise 对象,这点和我们的原来的 resolve() 方法可有点区别。我们先来看看源码的实现情况:
myPromise.resolve = function(result) {
// 如果传递的值为一个 Promise 对象, 则直接返回该对象, 否则返回一个 `fulfilled` 状态的新 `Promise` 对象
return result instanceof myPromise ? result : new myPromise(resolve => resolve(result));
}
看完源码的实现,我们再回过头来想想原来 resolve() 方法的问题,我们先跑个例子先:
// Promise
let p1 = new Promise((resolve, reject) => {
console.log(resolve('橙某人')); // undefined
})
let p2 = new Promise((resolve, reject) => {
console.log(resolve(p1)); // undefined
})
p1.then(r1 => {
console.log(r1); // 橙某人
})
p2.then(r2 => {
console.log(r2); // 橙某人
})
// myPromise
let p1 = new myPromise((resolve, reject) => {
console.log(resolve('橙某人')); // undefined
})
let p2 = new myPromise((resolve, reject) => {
console.log(resolve(p1)); // undefined
})
p1.then(r1 => {
console.log(r1); // 橙某人
})
p2.then(r2 => {
console.log(r2); // myPromise {...}
})
对比官方的 Promise 和我们自己的 myPromise 对象的输出,就能知道我们写的 resolve() 方法还是存在点问题,返回的结果都是 undefined 这点就没啥区别了,但是对于传入的值还要验证一下。
function myPromise(executor) {
...
function resolve(result) {
// 如果传递的 result 是一个 Promise 对象, 则需要返回这个 Promise 对象, 并且取出这个 Promise 对象的值, 继续传递下去
if(result instanceof myPromise) {
result.then(resolve, reject);
return;
}
if (_this.status === 'pending') {
...
}
}
...
}
reject
Promise.reject 方法也是 Promise 对象的静态方法,和上面的 Promise.resolve 差不多,但它不用考虑验证参数这一步,我们直接看源码:
myPromise.reject = function(reason) {
return new myPromise((resolve, reject) => reject(reason));
}
all
Promise.all 方法也是 Promise 对象的静态方法,比较常见了,经常应用于开发并发请求的功能中。它接收一个 Promise 对象的数组,如果数组项是普通值,则内部也会把它包装成一个 Promise 对象,并返回一个新的 Promise 对象;新返回的 Promise 对象会等待数组全部项都进入 fulfilled 状态,它才会进入 fulfilled 状态。我们先看看它的使用:
// 例子一
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('橙某人1');
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('橙某人2');
}, 2000)
})
let p = Promise.all([p1, 1, p2]);
p.then(r => {
console.log(r); // ["橙某人1", 1, "橙某人2"]
}).catch(e => {
console.log(e)
})
// 例子二
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('橙某人1');
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('出错');
}, 2000)
})
let p = Promise.all([p1, 1, p2]);
p.then(r => {
console.log(r);
}).catch(e => {
console.log(e); // 出错
})
源码实现过程:
myPromise.all = function(promiseArr) {
if(!Array.isArray(promiseArr)) return console.error('Parameter requires an Array');
let result = [];
let count = 0;
// 返回一个新的 Promise 对象
return new myPromise((resolve, reject) => {
promiseArr.forEach(p => {
// 把数组每项都传入 myPromise.resolve 方法中, 如果是 Promise 对象则直接返回, 如果不是 Promise 对象则会被包装成 Promise 对象返回
myPromise.resolve(p).then(r => {
result.push(r);
count++;
if(count === promiseArr.length) {
resolve(result);
}
}, e => {
reject(e); // 只要一个失败就把 Promise 对象直接改成失败状态
})
})
})
}
race
Promise.race 和 Promise.all 一样接收一个 Promise 对象的数组,也返回一个新的 Promise 对象,一旦数组中的某个项进入 fulfilled 或者 rejected 状态,新 Promise 对象的也会相应进入该状态。它基本和 Promise.all 差不多就不多讲了,直接上源码:
myPromise.race = function(promiseArr) {
if(!Array.isArray(promiseArr)) return console.error('Parameter requires an Array');
return new myPromise((resolve, reject) => {
promiseArr.forEach(p => {
myPromise.resolve(p).then(r => {
resolve(r);
}, e => {
reject(e);
})
})
})
}
完整源码
// 定义构造函数
function myPromise(executor) {
var _this = this;
_this.status = 'pending'; // status有三种状态: pending || fulfilled || rejected
_this.result = undefined; // 保存调用 resolve(result) 方法传递的值
_this.reason = undefined; // 保存调用 reject(reason) 方法传递的值
_this.onFulfilledCbs = []; // 调用 then(onFulfilled, onRejected) 方法时, 如果 status 为 pending 状态, 说明 resolve() 为异步调用, 所以 Promise 状态还没改变, 则先将 onFulfilled 回调函数存储起来
_this.onRejectedCbs = []; // 调用 then(onFulfilled, onRejected) 方法时, 如果 status 为 pending 状态, 说明 reject() 为异步调用, 所以 Promise 状态还没改变, 则先将 onRejected 回调函数存储起来
// 定义 reject()
function resolve(result) {
// 如果传递的 result 是一个 Promise 对象, 则需要返回这个 Promise 对象, 并且取出这个 Promise 对象的值, 继续传递下去
if(result instanceof myPromise) {
result.then(resolve, reject);
return;
}
if (_this.status === 'pending') {
_this.result = result;
_this.status = 'fulfilled';
// 执行所有 then(fulfillCbs, rejectedCbs) 方法的 onFulfilled
while (_this.onFulfilledCbs.length) {
_this.onFulfilledCbs.shift()(); // 按 then() 方法的调用顺序执行并删除
}
}
}
// 定义 reject()
function reject(reason) {
if (_this.status === 'pending') {
_this.reason = reason;
_this.status = 'rejected';
// 执行所有 then() 方法的 onRejected
while (_this.onRejectedCbs.length) {
_this.onRejectedCbs.shift()(); // 按 then() 方法的调用顺序执行并删除
}
}
}
// 立即执行 executor(), 执行 executor() 过程可能会出错, 所以要严谨进行try/catch一下, 并且错误结果能给 Promise 捕获
try {
executor(resolve, reject);
} catch (err) {
reject(err)
}
}
/**
* then() 方法的参数都是可选的
* @param {*} onFulfilled
* @param {*} onRejected
* @returns
*/
myPromise.prototype.then = function (onFulfilled, onRejected) {
// 解决值穿透
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : result => result;
onRejected = typeof onRejected === 'function' ? onRejected : reason => {
throw reason
};
// 返回一个新的 Promise 对象
var p = new myPromise((resolve, reject) => {
if (this.status === 'fulfilled' && typeof onFulfilled === 'function') {
// .then 调用是一个异步过程
setTimeout(() => {
try {
let x = onFulfilled(this.result);
// 借助 resolvePromise() 统一验证 x
resolvePromise(p, x, resolve, reject);
} catch (e) {
reject(e)
}
})
}
if (this.status === 'rejected' && typeof onRejected === 'function') {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(p, x, resolve, reject);
} catch (e) {
reject(e)
}
})
}
if (this.status === 'pending') {
if (typeof onFulfilled === 'function') {
/**
* 之所以 push 一个函数, 主要是为了回调函数能传值.
* 也可以直接 push 函数, 传值交由上面来完成:
* this.onFulfilledCbs.push(onFulfilled);
* _this.onFulfilledCbs.shift()(_this.result)
*/
this.onFulfilledCbs.push(() => {
try {
let x = onFulfilled(this.result);
resolvePromise(p, x, resolve, reject);
} catch (e) {
reject(e)
}
})
}
if (typeof onRejected === 'function') {
this.onRejectedCbs.push(() => {
try {
let x = onRejected(this.reason);
resolvePromise(p, x, resolve, reject);
} catch (e) {
reject(e)
}
})
}
}
})
return p;
}
/**
* 验证 x 的类型
* @param {*} p: 新返回的 Promise 对象
* @param {*} x: onFulfilled or onRejected 返回的值
* @param {*} resolve: 新 Promise 对象的 resolve 方法
* @param {*} reject: 新 Promise 对象的 reject 方法
* @returns
*/
function resolvePromise(p, x, resolve, reject) {
// 1. 如果 x 为新返回的 Promise 对象, 则直接抛出错误
if (x != undefined && p === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}
// 2. 如果 x 为另一个 myPromise 对象, 也就是显式 return new myPromise()
if (x instanceof myPromise) {
if (x.status === 'pending') { // 如果 x 这个 myPromise 对象处于 pending 状态, 则要等待直 x 被执行或拒绝
x.then(r => {
resolvePromise(p, r, resolve, reject); // 递归等待 x 被执行
}, e => {
reject(e);
})
} else { // 如果 x 这个 myPromise 对象已经处于 fulfilled or rejected 状态, 则直接再次调用 then() 方法!
x.then(resolve, reject); // 这里的意思其实可以理解为, 我们执行了新的 myPromise 对象的 then() 方法, 它的两个回调参数, 就是我们下一个 then() 两个回调的参数.
}
return;
}
// 3. 如果 x 为对象或者函数, 如果不是就是普通的数据类型, 直接通过
var called; // 只调用一次, 防止重复调用
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 如果 x.then 为一个函数, 则我们会执行这个函数, 并且函数内部的 this 指向 x, 执行这个函数内部可能会有错误, 所以需要 try/catch 一下
try {
let then = x.then; // 获取 x 身上的 then() 方法
if (typeof then === 'function') {
then.call(x, function (res) {
if (called) return;
called = true;
// 可能 res 还是一个 promise 对象, 所以需要递归下去判断 res(也就是x) 的类型
resolvePromise(p, res, resolve, reject); // 递归
}, function (err) {
if (called) return;
called = true;
reject(err)
})
} else {
resolve(x); // 普通值, x.then 可能是 x.then = '橙某人
}
} catch (e) {
reject(e)
}
} else {
resolve(x); // 普通值
// 这里为什么直接就调用 resolve() 不考虑 reject(), 因为规定错误不能通过 return 来传递, 只能通过 throw 的形式, 也就是 onRejected 中 return 是无效的
}
}
/**
* 捕获异常, 本质是 onRejected 参数
* @param {*} onRejected
* @returns
*/
myPromise.prototype.catch = function (onRejected) {
// 相当于再次调用了一次 then() 方法, 把传进来的参数作为了 then() 方法的第二个参数
return this.then(null, onRejected)
}
/**
* 返回一个 `fulfilled` 状态的新 `Promise` 对象。
* @param {*} result
* @returns
*/
myPromise.resolve = function(result) {
// 如果传递的值为一个 Promise 对象, 则直接返回该对象, 否则返回一个 `fulfilled` 状态的新 `Promise` 对象
return result instanceof myPromise ? result : new myPromise(resolve => resolve(result));
}
/**
* 返回一个 `rejected` 状态的新 `Promise` 对象。
* @param {*} result
* @returns
*/
myPromise.reject = function(reason) {
return new myPromise((resolve, reject) => reject(reason));
}
myPromise.all = function(promiseArr) {
if(!Array.isArray(promiseArr)) return console.error('Parameter requires an Array');
let result = [];
let count = 0;
// 返回一个新的 Promise 对象
return new myPromise((resolve, reject) => {
promiseArr.forEach(p => {
// 把数组每项都传入 myPromise.resolve 方法中, 如果是 Promise 对象则直接返回, 如果不是 Promise 对象则会被包装成 Promise 对象返回
myPromise.resolve(p).then(r => {
result.push(r);
count++;
if(count === promiseArr.length) {
resolve(result);
}
}, e => {
reject(e); // 只要一个失败就把 Promise 对象直接改成失败状态
})
})
})
}
myPromise.race = function(promiseArr) {
if(!Array.isArray(promiseArr)) return console.error('Parameter requires an Array');
return new myPromise((resolve, reject) => {
promiseArr.forEach(p => {
myPromise.resolve(p).then(r => {
resolve(r);
}, e => {
reject(e);
})
})
})
}
至此,本篇文章就写完啦,撒花撒花。
希望本文对你有所帮助,如有任何疑问,期待你的留言哦。
老样子,点赞+评论=你会了,收藏=你精通了。