theme: smartblue
根据Promise/A+ 规范简单实现一个Promise
最近在复习准备面试,重温了一下Promise实现,简单说说我自己的一个实现思路
1. Promise/A+ 规范
这里只列举一些比较核心的
-
promise
状态必须是这三个状态中的一种:等待态pending
,解决态fulfilled
或拒绝态rejected
-
promise
状态是不可逆的,状态的变更是单向的,只能从Pending -> Fulfilled 或 Pending -> Rejected -
Promise
必须提供一个then
方法来访问当前或最终的值或原因。Promise
的then
方法接受俩个参数:promise.then(onFulfilled, onRejected)
- 如果
onFulfilled
不是一个函数,它必须被忽略,如果onRejected
不是一个函数,它必须被忽略 - then方法返回一个promise,then 方法可以被同一个 promise 调用多次。
2. 原生Promise使用思路
//创建一个Promise
let promise = new Promise((resolve, reject)=>{
resolve('result');
});
promise.then(
result => console.log(result),
err => console.log(err)
);
根据上面代码,我们分析一下:
Promise
是new出来的,说明它是一个构造函数Promise
可接收一个函数,函数在new Promise()
时立即执行- 函数参数为
resolve
和reject
,代表成功/失败方法 then()
方法可传入收集成功/失败回调,并且根据Promise/A+ 规范,它可返回一个Promise
,可链式调用
3. 根据思路实现Promise
根据上面思路,我们一步步去进行实现
1. 使用class实现,实现传入方法并执行
//实现Promise
class NewPromise {
constructor(fn) {
this.result = null; //定义result存储传入值
//传入方法直接执行
fn(this.resolve, this.reject);
}
//定义成功失败方法,需注意使用箭头函数写法,this指向对象内部
resolve = (result) => {
this.result = result; //接收传入值
}
reject = (result) => {
this.result = result;
}
}
2. 根据Promise/A+ 规范,定义状态值
//实现Promise
class NewPromise {
//定义Promise状态值
static PENDING = '待定'; static FULFILLED = '解决'; static REJECTED = '拒绝';
constructor(fn) {
this.result = null; //定义result存储传入值
this.status = NewPromise.PENDING; //定义初始化状态,初始化状态为待定
//传入方法直接执行
fn(this.resolve, this.reject);
}
//定义成功失败方法,需注意使用箭头函数写法,this指向对象内部
resolve = (result) => {
//状态是单向且不可逆的,所以如果判断不为待定态则return
if (this.status !== NewPromise.PENDING) return;
this.status = NewPromise.FULFILLED; //改变状态为对应状态
this.result = result; //接收传入值
}
reject = (result) => {
if (this.status !== NewPromise.PENDING) return;
this.status = NewPromise.REJECTED;
this.result = result;
}
}
3. 实现then方法
//实现Promise
class NewPromise {
...
then = (onResolve, onReject) => {
//根据状态使用对应方法
switch (this.status) {
case NewPromise.FULFILLED:
onResolve(this.result);
break;
case NewPromise.REJECTED:
onReject(this.result);
break;
}
}
}
这时候我们测试一下代码
//简单测试一下
let promise = new NewPromise((resolve, reject) => {
resolve('result');
});
promise.then(
result => console.log(result),
err => console.log(err)
); //输出了result,结果比较正常
//但是我们换种写法
let promise = new NewPromise((resolve, reject) => {
console.log('第一步');
resolve('第二步');
});
promise.then(
result => console.log(result),
err => console.log(err)
);
console.log('第三步'); //输出结果为 第一步 第二步 第三步
//我们测试下原生Promise的
let promise = new Promise((resolve, reject) => {
console.log('第一步');
resolve('第二步');
});
promise.then(
result => console.log(result),
err => console.log(err)
);
console.log('第三步'); //输出结果为 第一步 第三步 第二步
从测试发现,我们的输出结果与原生Promise
有所不同,问题很简单,then是微任务,需要异步执行,我们二话不说,直接给then里面回调方法加上setTimeout
//实现Promise
class NewPromise {
...
then = (onResolve, onReject) => {
//根据状态使用对应方法
switch (this.status) {
case NewPromise.FULFILLED:
setTimeout(() => {
onResolve(this.result);
});
break;
case NewPromise.REJECTED:
setTimeout(() => {
onReject(this.result);
});
break;
}
}
}
这时候的测试结果就正常了,但是问题其实还没有解决,我们再举个例子,在resolve
外层包裹个setTimeout,使得resolve
异步执行
let promise = new NewPromise((resolve, reject) => {
console.log('第一步');
setTimeout(() => {
resolve('第二步');
});
});
promise.then(
result => console.log(result),
err => console.log(err)
);
console.log('第三步'); //输出结果为 第一步 第三步,缺少了第二步
代码执行到then的时候出现了问题,并没有输出第二步,我们调试代码就会发现,当resolve
变成异步时,执行到then
时Promise
为PENDING状态,而我们then
方法里面并没有对于PENDING状态的处理,所以我们需要在then里面去添加一个PENDING状态处理,但是我们要考虑以下几点:
then
在PENDING状态下,因为resolve
与reject
都没有触发过,所以没有获取到result
值- 将
then
里面的onResolve
、onReject
存储起来,待resolve
、reject
执行后再执行
考虑完这几个点后,我们进行实现
//实现Promise
class NewPromise {
//定义Promise状态值
static PENDING = '待定'; static FULFILLED = '解决'; static REJECTED = '拒绝';
constructor(fn) {
this.result = null; //定义result存储传入值
this.status = NewPromise.PENDING; //定义初始化状态,初始化状态为待定
//创建成功/失败数组存储成功/失败回调
this.resolveCallback = [];
this.rejectCallback = [];
//传入方法直接执行
fn(this.resolve, this.reject);
}
//定义成功失败方法,需注意使用箭头函数写法,this指向对象内部
resolve = (result) => {
//添加定时器,确保resolve是在事件循环末尾执行
setTimeout(() => {
//状态是单向且不可逆的,所以如果判断不为待定态则return
if (this.status !== NewPromise.PENDING) return;
this.status = NewPromise.FULFILLED; //改变状态为对应状态
this.result = result; //接收传入值
this.resolveCallback.forEach(callback => {
callback(result);
})
});
}
reject = (result) => {
setTimeout(() => {
if (this.status !== NewPromise.PENDING) return;
this.status = NewPromise.REJECTED;
this.result = result;
this.rejectCallback.forEach(callback => {
callback(result);
})
});
}
then = (onResolve, onReject) => {
//根据状态使用对应方法
switch (this.status) {
case NewPromise.PENDING: //添加待定状态处理,存储成功失败回调
this.resolveCallback.push(onResolve);
this.rejectCallback.push(onReject);
break;
case NewPromise.FULFILLED:
setTimeout(() => {
onResolve(this.result);
});
break;
case NewPromise.REJECTED:
setTimeout(() => {
onReject(this.result);
});
break;
}
}
}
4. .then()链式调用
根据Promise/A+ 规范,.then()
需要返回一个Promise
,并且.then()
需要拿到上一个.then()
的返回值,我们按照这个思路简单实现一下
//实现Promise
class NewPromise {
...
then = (onResolve, onReject) => {
return new NewPromise((resolve, reject) => { //返回一个Promise
// 简单处理下成功/失败回调
const resolveFn = res => {
const x = onResolve(res); //执行当前Promise的成功回调,获取返回值
resolve(x); //执行resolve,下一个then就能获取到x值
}
const rejectFn = err => {
const x = onReject(err); //执行当前Promise的成功回调,获取返回值
resolve(x); //执行resolve,下一个then就能获取到x值
}
//根据状态使用对应方法
switch (this.status) {
case NewPromise.PENDING: //添加待定状态处理,存储成功失败回调
this.resolveCallback.push(resolveFn);
this.rejectCallback.push(rejectFn);
break;
case NewPromise.FULFILLED:
setTimeout(() => {
onResolve(this.result);
});
break;
case NewPromise.REJECTED:
setTimeout(() => {
onReject(this.result);
});
break;
}
})
}
}
上面我们简单实现了链式调用,但是考虑得还不够前面,成功/失败的x值不为Promise
对象时,才能直接使用用resolve,如果x的值为应该Promise
对象,我们需要判断它的成功/失败状态去调用对应的方法
//实现Promise
class NewPromise {
...
then = (onResolve, onReject) => {
return new NewPromise((resolve, reject) => { //返回一个Promise
// 简单处理下成功/失败回调,同时继续报错处理
const resolveFn = res => {
try {
const x = onResolve(res); //执行当前Promise的成功回调,获取返回值
//这里进行原型判断,如果x为Promise对象,则进行then操作,根据Promise状态进行成功/失败处理,否则直接使用resolve
x instanceof NewPromise ? x.then(resolve, reject) : resolve(x);
} catch (e) {
reject(e);
}
}
const rejectFn = err => {
try {
const x = onReject(err);
x instanceof NewPromise ? x.then(resolve, reject) : resolve(x);
} catch (e) {
reject(e);
}
}
//根据状态使用对应方法
switch (this.status) {
case NewPromise.PENDING: //添加待定状态处理,存储成功失败回调
this.resolveCallback.push(resolveFn);
this.rejectCallback.push(rejectFn);
break;
case NewPromise.FULFILLED:
setTimeout(() => {
onResolve(this.result);
});
break;
case NewPromise.REJECTED:
setTimeout(() => {
onReject(this.result);
});
break;
}
})
}
}
5. 值穿透处理
根据规范,如果 .then()
接收的参数不是function,那么我们应该忽略它。如果没有忽略,当.then()
回调不为function时将会抛出异常,导致链式调用中断
//实现Promise
class NewPromise {
...
then = (onResolve, onReject) => {
// 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
typeof onResolve !== 'function' ? onResolve = value => value : null;
typeof onReject !== 'function' ? onReject = reason => {
throw new Error(reason instanceof Error ? reason.message : reason);
} : null;
return new NewPromise((resolve, reject) => { //返回一个Promise
// 简单处理下成功/失败回调,同时继续报错处理
const resolveFn = res => {
try {
const x = onResolve(res); //执行当前Promise的成功回调,获取返回值
//这里进行原型判断,如果x为Promise对象,则进行then操作,根据Promise状态进行成功/失败处理,否则直接使用resolve
x instanceof NewPromise ? x.then(resolve, reject) : resolve(x);
} catch (e) {
reject(e);
}
}
const rejectFn = err => {
try {
const x = onReject(err);
x instanceof NewPromise ? x.then(resolve, reject) : resolve(x);
} catch (e) {
reject(e);
}
}
//根据状态使用对应方法
switch (this.status) {
case NewPromise.PENDING: //添加待定状态处理,存储成功失败回调
this.resolveCallback.push(resolveFn);
this.rejectCallback.push(rejectFn);
break;
case NewPromise.FULFILLED:
setTimeout(() => {
onResolve(this.result);
});
break;
case NewPromise.REJECTED:
setTimeout(() => {
onReject(this.result);
});
break;
}
})
}
}
完整代码
//实现Promise
class NewPromise {
//定义Promise状态值
static PENDING = '待定'; static FULFILLED = '解决'; static REJECTED = '拒绝';
constructor(fn) {
this.result = null; //定义result存储传入值
this.status = NewPromise.PENDING; //定义初始化状态,初始化状态为待定
//创建成功/失败数组存储成功/失败回调
this.resolveCallback = [];
this.rejectCallback = [];
//传入方法直接执行
fn(this.resolve, this.reject);
}
//定义成功失败方法,需注意使用箭头函数写法,this指向对象内部
resolve = (result) => {
//添加定时器,确保resolve是在事件循环末尾执行
setTimeout(() => {
//状态是单向且不可逆的,所以如果判断不为待定态则return
if (this.status !== NewPromise.PENDING) return;
this.status = NewPromise.FULFILLED; //改变状态为对应状态
this.result = result; //接收传入值
this.resolveCallback.forEach(callback => {
callback(result);
})
});
}
reject = (result) => {
setTimeout(() => {
if (this.status !== NewPromise.PENDING) return;
this.status = NewPromise.REJECTED;
this.result = result;
this.rejectCallback.forEach(callback => {
callback(result);
})
});
}
then = (onResolve, onReject) => {
// 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
typeof onResolve !== 'function' ? onResolve = value => value : null;
typeof onReject !== 'function' ? onReject = reason => {
throw new Error(reason instanceof Error ? reason.message : reason);
} : null;
return new NewPromise((resolve, reject) => { //返回一个Promise
// 简单处理下成功/失败回调,同时继续报错处理
const resolveFn = res => {
try {
const x = onResolve(res); //执行当前Promise的成功回调,获取返回值
//这里进行原型判断,如果x为Promise对象,则进行then操作,根据Promise状态进行成功/失败处理,否则直接使用resolve
x instanceof NewPromise ? x.then(resolve, reject) : resolve(x);
} catch (e) {
reject(e);
}
}
const rejectFn = err => {
try {
const x = onReject(err);
x instanceof NewPromise ? x.then(resolve, reject) : resolve(x);
} catch (e) {
reject(e);
}
}
//根据状态使用对应方法
switch (this.status) {
case NewPromise.PENDING: //添加待定状态处理,存储成功失败回调
this.resolveCallback.push(resolveFn);
this.rejectCallback.push(rejectFn);
break;
case NewPromise.FULFILLED:
setTimeout(() => {
onResolve(this.result);
});
break;
case NewPromise.REJECTED:
setTimeout(() => {
onReject(this.result);
});
break;
}
})
}
}
上面只是实现了Promise
的一个基本功能,要扩展别的功能其实知道原理的话,很容易实现,比如.catch()
//实现Promise
class NewPromise {
...
catch = (reject) => {
return this.then(undefined, reject);
}
}
把原理弄懂弄透的话,手写出Promise
其实很简单