我们要用原生js实现Promise类的话,就得非常熟悉Promise 的规则,以及一些属性方法的应用。
一、搭建Promise
首先我们确定Promise使用过程中有三个状态,那么我们可以先声明三个常量来表示Promise的状态 如下所示:
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
那么我们为什么要这么声明常量的命名格式呢,这是因为我遵循了Promise A+的规范;
二、框架的搭建
我们先搭建Promise的框架,写一个新的类,名字就叫做zbPromise,如下所示
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_REJECTED = 'rejected'
class zbPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
const resolve = () => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
console.log('resolve被执行了');
}
}
const reject = () => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
console.log('reject被执行了');
}
}
executor(resolve, reject)
}
}
const p1 = new zbPromise((resolve, reject) => {
resolve('success')
})
Executor是在创建Promise时需要传入的一个回调函数,这个回调函数会被立即执行,并且传入两个参数: 也就是这里的resolve和reject函数
可以运行上面的代码,在new zbPromise之后,执行resolve函数,执行结果如下:
三、then方法的实现
现在我们接着来实现then方法
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_REJECTED = 'rejected'
class zbPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilled(this.value)
console.log('resolve被执行了');
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejected(this.reason)
console.log('reject被执行了');
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
}
const p1 = new zbPromise((resolve, reject) => {
resolve('success')
})
p1.then(res => {
console.log(res);
}, err => {
console.log(err);
})
此时执行代码会发现报错,我们new Promise的时候,会自动执行constructor构造函数,但是当执行resolve(success')的时候,本应该是执行完resolve函数,然后由then回调执行的结果,但是它在执行resolve的时候就报错了,是因为resolve的时候此时并没有onFulfilled函数传进来,所以我们可以将整个resolve的执行逻辑包裹在微任务队列里面,这样一来就可以正常执行了。
正确代码:
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_REJECTED = 'rejected'
class zbPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilled(this.value)
console.log('resolve被执行了');
})
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejected(this.reason)
console.log('reject被执行了');
})
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
}
const p1 = new zbPromise((resolve, reject) => {
resolve('success')
})
p1.then(res => {
console.log(res);
}, err => {
console.log(err);
})
执行结果如下:
3.1then方法的优化1------实现promise实例的多次调用
虽然我们已经实现了promise的then方法,但是当我们多次调用的then的时候,它的最后一次结果会覆盖前面所有的结果,并且我们在new zbPromise函数的时候,同时调用reject函数的话,那么reject函数也会被执行,这与我们的意愿相互违背,按理来说,promise的状态在被修改以后是不会被再次改变的。于是我们有了一个想法,那就是在then方法中用两个函数数组来保存所要执行的函数,然后在resolve或者reject函数中将这两个数组进行遍历执行,然后在微任务队列函数中加入状态判断,如果不是pending状态,那就是直接return,不需要在继续执行下去。
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_REJECTED = 'rejected'
class zbPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
console.log('resolve被执行了');
})
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
console.log('reject被执行了');
})
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
// this.onFulfilled = onFulfilled
// this.onRejected = onRejected
this.onFulfilledFns.push(onFulfilled)
this.onRejectedFns.push(onRejected)
}
}
const p1 = new zbPromise((resolve, reject) => {
resolve('success')
//当同时出现的时候,谁先执行,就先回调谁的结果,且只执行一次
reject('failure')
})
p1.then(res => {
console.log(res);
}, err => {
console.log(err);
})
p1.then(res => {
console.log(res);
}, err => {
console.log(err);
})
执行结果:
3.2 then的优化2-----执行完一次以后,我们直接执行对应的函数
如果在我们then调用的时候,状态已经确定下来了,那么我们直接执行对应的函数即可
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_REJECTED = 'rejected'
class zbPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
console.log('resolve被执行了');
})
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
console.log('reject被执行了');
})
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
// this.onFulfilled = onFulfilled
// this.onRejected = onRejected
// 如果状态已经是fulfilled了,那么就直接执行onfulfilled
if (this.onFulfilled && this.status === PROMISE_STATUS_FULFILLED) {
onFulfilled(this.value)
}
if (this.onRejected && this.status === PROMISE_STATUS_REJECTED) {
onRejected(this.reason)
}
if (this.status === PROMISE_STATUS_PENDING) {
this.onFulfilledFns.push(onFulfilled)
this.onRejectedFns.push(onRejected)
}
}
}
const p1 = new zbPromise((resolve, reject) => {
resolve('success')
reject('failure')
})
p1.then(res => {
console.log(res);
}, err => {
console.log(err);
})
p1.then(res => {
console.log(res);
}, err => {
console.log(err);
})
3.3 then优化3----链式调用(then的时候返回新的promise)
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_REJECTED = 'rejected'
class zbPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
})
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
// this.onFulfilled = onFulfilled
// this.onRejected = onRejected
// 如果状态已经是fulfilled了,那么就直接执行onfulfilled
return new zbPromise((resolve, reject) => {
if (this.onFulfilled && this.status === PROMISE_STATUS_FULFILLED) {
try {
const value = onFulfilled(this.value)
resolve(value)
} catch (error) {
reject(error)
}
}
if (this.onRejected && this.status === PROMISE_STATUS_REJECTED) {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch (error) {
reject(error)
}
}
if (this.status === PROMISE_STATUS_PENDING) {
this.onFulfilledFns.push(() => {
try {
const value = onFulfilled(this.value)
resolve(value)
} catch (error) {
reject(error)
}
})
//我们将函数的用trycatch代码块包裹起来,拿到某一状态的执行结果,将结果返回传入到相应的resolve和reject中
this.onRejectedFns.push(() => {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch (error) {
reject(error)
}
})
}
})
}
}
const p1 = new zbPromise((resolve, reject) => {
resolve('success')
reject('failure')
})
p1.then(res => {
console.log(res);
// throw new Error('我第一次执行出错了')
return 'aaaaaa'
}, err => {
console.log(err);
}).then(res => {
console.log(res);
}, err => {
console.log(err);
})
p1.then(res => {
console.log(res);
}, err => {
console.log(err);
})
四、catch方法的实现
由于是catch方法,能够回调出reject的结果,那么catch方法的函数体中我们可以用then来实现, 比如
catch(onRejected){
this.then(undefined,onRejected)
}
这样能行吗?理论上是可行的,但是这样虽然能执行catch方法,但是输出为undefined,因为catch方法相当于调用了一个新的promise,而我们的目的是回调前一个Promise的失败的回调,我们该怎么解决呢?
那么我们可以直接将第一个promise失败的回调直接抛出异常,那么第二个刚好可以执行;
于是我们在then方法中加入
onRejected = onRejected || (err => { throw err })
也就是如果第一个Promise的onRejected为undefined的话,那么我们就抛出异常;
五、finally方法的实现
不管是成功还是失败,都会调用finally方法,那么就简单实现了(注意在此之前一定要把catch方法里面的值返回,finally用于接收)
finally(onFinally) {
this.then(() => {
onFinally()
}, () => {
onFinally()
})
}
这样一来的话,我们就必须在then方法中加入onFulfilled判断,由于finally方法下相当于第三个promise回调,所以我们要将前面then方法中加入 onFulfilled = onFulfilled || (value => value) onFulfilled为空的话,就返回第一个promise执行结果。
六、resolve方法和reject方法
直接返回promise即可,调用对应的方法,注意这两个是类方法,不是实例方法
static resolve(value) {
return new zbPromise((resolve) => { resolve(value) })
}
static reject(reason) {
return new zbPromise(reject => reject(reason))
}
七、all方法
执行promise数组,其中只要不是reject状态,那么就会执行,否则就会停止,调用catch方法;
static all(promises) {
return new zbPromise((resolve, reject) => {
const values = []
promises.forEach(promise => {
promise.then(res => {
values.push(res)
if (values.length === promise.length) {
resolve(values)
}
// console.log(res);
}, err => {
// console.log(err);
reject(err)
})
})
})
}
八、allsettled方法
不管什么状态,都会输出回调结果
static allSettled(promises) {
return new zbPromise((resolve, reject) => {
const values = []
promises.forEach(promise => {
promise.then(res => {
values.push({ status: PROMISE_STATUS_FULFILLED, result: res })
if (values.length === promises.length) {
resolve(values)
}
}, err => {
values.push({ status: PROMISE_STATUS_REJECTED, result: err })
if (values.length === promises.length) {
resolve(values)
}
})
})
})
}