前言
- 不针对 Promise A+ 规范实现。
- 不考虑异步,因为要实现的话需要基于 generator,比较复杂。
- 只针对在某个现实场景中需要手写,所以会简化,不使用太复杂的方式去实现,能满足简单功能。
- 因为目的不是真的要你写个大而全 Promise,只是要在有限的时间里检验你的代码功底和对 Promise 的了解。
当我们在做题的时候,特别是面对一个边界比较模糊的问题,需要对解决问题的结果具象化,首先我们先拟定,我们要实现 Promise 的如下功能。
MyPromise.resolve(1).then((res) => {
return 1 + res
}).then((res) => {
console.log(res)
})
// 输出 2
const p = new MyPromise((resolve, reject) => {
resolve(1)
}).then((res) => {
console.log(res)
throw 'error'
}).catch((e) => {
console.log(e)
})
// 先输出 1,再输出 error
- 那么我们接下来的解法可以如下:
class MyPromise {
// 留意构造函数是接收一个函数,函数的两个参数函数是实参,留意不要写错
constructor(cb) {
// 注意有 3 中状态用来判断接下来 then 和 catch 的执行
this.states = {
PENDING: 'PENDING',
RESOLVED: 'RESOLVED',
REJECTED: 'REJECTED'
}
// 默认开始的状态为 pending
this.state = this.states.PENDING;
// 用来做 then catch 之间的 value 的扭转
this.value = null;
// 对 cb 回调函数的两个实参的实现
const resolve = (val) => {
this.value = val;
this.state = this.states.RESOLVED;
}
const reject = (val) => {
this.value = val;
this.state = this.states.REJECTED;
}
// 注意,在执行 cb 回调的时候,也要去捕获错误,满足就需要更改状态
try {
cb(resolve, reject);
} catch (e) {
reject(e)
}
}
// 注意 resolve 和 reject 是两个静态函数,但是要接 then 所有要返回一个实例
static resolve(val) {
const promise = new MyPromise((res) => res(val))
promise.value = val;
promise.state = promise.states.RESOLVED;
return promise;
}
static reject(val) {
const promise = new MyPromise((res, rej) => rej(val))
promise.value = val;
promise.state = promise.states.REJECTED;
return promise;
}
// then 有两个参数,分别对应 resolve 和 reject 的回调,注意判断两个函数的执行条件
then(resFn, rejFn) {
// 需要捕获错误
try {
// 是函数且状态不是被 reject
if (typeof resFn === 'function' && this.state !== this.states.REJECTED) {
// 留意这里因为下一次 then 的 value 是上一次 then 返回的值,故需要做此操作
this.value = resFn(this.value)
}
// 假如有错误,直接进入到 reject 状态
} catch (e) {
this.state = this.states.REJECTED;
this.value = e;
return this;
}
// 同理
if (typeof rejFn === 'function' && this.state === this.states.REJECTED) {
this.state = this.states.REJECTED;
this.value = rejFn(this.value)
}
return this;
}
// catch 仅当被 reject 的时候返回
catch(cb) {
if (this.state === this.states.REJECTED) {
this.value = cb(this.value)
}
return this;
}
}