之前的文章里讲了Promise的好处以及具体应用,不了解Promise用法的读者可以移步到这里:JavaScript|Promise使用详解。这里我们主要来看看Promise是怎么实现的。
Promise结构设计
如果要实现一个Promise,应该按照Promise A+规范来设计:Promise A+
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class myPromise{
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
console.log('resolve called');
this.value = value
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
console.log('reject called');
this.reason = reason
}
}
executor(resolve, reject) // 调用resolve/reject方法
}
}
const promise = new myPromise((resolve, reject) => {
console.log('执行了'); // 执行了
resolve() // resolve called
reject() // 要注意,这里不会调用reject方法
})
Promise.then方法设计
Promise.then方法是Promise的核心方法,它的作用是:当Promise的状态变为fulfilled或rejected时,执行then方法的回调函数,但是调用这个方法有需要考虑很多问题,如多次调用,链式调用,以及调用时Promise的状态等等。
then方法基本结构
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class myPromise{
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
queueMicrotask(() => { // 异步执行,先执行then方法,将onFulfilled方法放入队列中
this.value = value
this.onfulfilled&&this.onfulfilled(value)
})
}
}
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
queueMicrotask(() => {
this.reason = reason
this.onrejected&&this.onrejected(reason)
})
}
}
executor(resolve, reject) // 调用resolve/reject方法
}
then(onfulfilled, onrejected) {
this.onfulfilled = onfulfilled
this.onrejected = onrejected
}
}
const promise = new myPromise((resolve, reject) => {
console.log('执行了'); // 执行了
resolve('res')
}).then(res => {
console.log(res) // res
}, err => {
console.log(err)
})
const promise1 = new myPromise((resolve, reject) => {
console.log('执行了'); // 执行了
reject('err')
}).then(res => {
console.log(res)
}, err => {
console.log(err) // err
})
then方法多次调用
上述实现的then虽然能够实现,但如果再次添加then方法的时候会覆盖掉onfulfilled和onrejected,所以需要添加一个多次调用的方法。这里我们将方法加入到数组中,然后遍历数组,执行每一个方法。
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
class myPromise{
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(() => { // 异步执行,先执行then方法,将onFulfilled方法放入队列中
if (this.status !== PROMISE_STATUS_PENDING) return // 如果状态已经变化,则不执行
this.status = PROMISE_STATUS_FULFILLED // 设置状态,放在微任务队列可以保证状态变化的时候,不会执行then方法
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.onfulfilledFns.forEach(fn => {
fn(this.reason)
})
})
}
}
executor(resolve, reject) // 调用resolve/reject方法
}
then(onfulfilled, onrejected) {
// 调用then时,状态确,例如setTimeout(() => {}, 0),那么就会立即执行onfulfilled方法,而不是将方法放入队列中
if (this.status === PROMISE_STATUS_FULFILLED) {
onfulfilled(this.value)
} else if (this.status === PROMISE_STATUS_REJECTED) {
onrejected(this.reason)
}
// 将成功回调和失败回调放入数组中
if (this.status === PROMISE_STATUS_PENDING) {
this.onfulfilledFns.push(onfulfilled)
this.onrejectedFns.push(onrejected)
}
}
}
const promise = new myPromise((resolve, reject) => {
console.log('执行了'); // 执行了
resolve('res')
reject('err')
})
// 调用then方法多次调用
promise.then(res => {
console.log("res1:", res)
}, err => {
console.log("err:", err)
})
promise.then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
// 在确定Promise状态之后, 再次调用then
setTimeout(() => {
promise.then(res => {
console.log("res3:", res)
}, err => {
console.log("err3:", err)
})
}, 1000)
then方法的链式调用
上述实现的then方法多次调用,但如果在一个then后面再次调用then方法,则会报错,原因是then没有返回,我们需要在then函数中添加一个返回值,返回一个新的myPromise对象。
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
// 抽离try catch函数储存返回值
function execFunctionWithCatchError(execFn, value, resolve, reject) {
try {
const reason = execFn(value)
resolve(reason)
} catch (error) {
reject(error)
}
}
class myPromise{
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(() => { // 异步执行,先执行then方法,将onFulfilled方法放入队列中
if (this.status !== PROMISE_STATUS_PENDING) return // 如果状态已经变化,则不执行
this.status = PROMISE_STATUS_FULFILLED // 设置状态,放在微任务队列可以保证状态变化的时候,不会执行then方法
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)
})
})
}
}
try { // 第一个promise可能报错,报错信息需要通过REJECT方法传递给第二个promise
executor(resolve, reject) // 调用resolve/reject方法
} catch (err) {
reject(err)
}
}
then(onfulfilled, onrejected) {
return new myPromise((resolve, reject) => { // 直接返回这个promise,如果没有then会直接执行
// 调用then时,状态确,例如setTimeout(() => {}, 0),那么就会立即执行onfulfilled方法,而不是将方法放入队列中
if (this.status === PROMISE_STATUS_FULFILLED && resolve) {
// try { //回调函数需要获取结果或者报错,所以需要try catch
// const value = onfulfilled(this.value)
// resolve(value)
// } catch (error) {
// reject(error)
// }
execFunctionWithCatchError(onfulfilled, this.value, resolve, reject)
} else if (this.status === PROMISE_STATUS_REJECTED && onrejected) {
execFunctionWithCatchError(onrejected, this.reason, resolve, reject)
}
// 将成功回调和失败回调放入数组中
if (this.status === PROMISE_STATUS_PENDING) {
this.onfulfilledFns.push(() => {
execFunctionWithCatchError(onfulfilled, this.value, resolve, reject)
})
this.onrejectedFns.push(() => {
execFunctionWithCatchError(onrejected, this.reason, resolve, reject)
})
}
})
}
}
const promise = new myPromise((resolve, reject) => {
console.log('执行了'); // 执行了
resolve('res')
reject('err')
})
// 调用then方法多次调用
promise.then(res => {
console.log("res1:", res)
return "aaaa"
// throw new Error("err message")
}, err => {
console.log("err1:", err)
return "bbbbb"
// throw new Error("err message")
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
Promise.catch方法设计
Promise.catch方法是Promise.prototype.then方法的简化版本,当Promise的状态变为rejected时,会调用参数函数,并将Promise的reason作为参数传入函数。在这里可以直接调用then方法来实现吗,除了调用then,还需要设置相关的边界条件处理undefined,并且要确保resolve和reject方法的返回值能够被下一个方法捕获到。
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
// 抽离try catch函数储存返回值
function execFunctionWithCatchError(execFn, value, resolve, reject) {
try {
const reason = execFn(value)
resolve(reason)
} catch (error) {
reject(error)
}
}
class myPromise{
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(() => { // 异步执行,先执行then方法,将onFulfilled方法放入队列中
if (this.status !== PROMISE_STATUS_PENDING) return // 如果状态已经变化,则不执行
this.status = PROMISE_STATUS_FULFILLED // 设置状态,放在微任务队列可以保证状态变化的时候,不会执行then方法
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)
})
})
}
}
try { // 第一个promise可能报错,报错信息需要通过REJECT方法传递给第二个promise
executor(resolve, reject) // 调用resolve/reject方法
} catch (err) {
reject(err)
}
}
then(onfulfilled, onrejected) {
// catch 传入 undefined 会抛出异常,让 promise 去调用下一个 reject
const defaultOnRejected = err => {throw err}
onrejected = onrejected || defaultOnRejected
// resolve 也需要返回
const defaultOnFulfilled = value => { value }
onfulfilled = onfulfilled || defaultOnFulfilled
return new myPromise((resolve, reject) => { // 直接返回这个promise,如果没有then会直接执行
// 调用then时,状态确,例如setTimeout(() => {}, 0),那么就会立即执行onfulfilled方法,而不是将方法放入队列中
if (this.status === PROMISE_STATUS_FULFILLED && onfulfilled) {
//回调函数需要获取结果或者报错,所以需要try catch
execFunctionWithCatchError(onfulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS_REJECTED && onrejected) {
execFunctionWithCatchError(onrejected, this.reason, resolve, reject)
}
// 将成功回调和失败回调放入数组中
if (this.status === PROMISE_STATUS_PENDING) {
// catch 会传入 undefined
if (onfulfilled) this.onfulfilledFns.push(() => {
execFunctionWithCatchError(onfulfilled, this.value, resolve, reject)
})
if (onrejected) this.onrejectedFns.push(() => {
execFunctionWithCatchError(onrejected, this.reason, resolve, reject)
})
}
})
}
catch(onrejected) {
return this.then(undefined, onrejected) // 将实例返回出来,以便下一个方法调用
}
}
const promise = new myPromise((resolve, reject) => {
console.log('执行了'); // 执行了
// resolve('res')
reject('err')
})
// 调用catch方法
promise.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err) // err: err
})
Promise.finally方法设计
Promise.finally的实现可以直接在将then方法下的两个参数传入finally的方法实现:
class myPromise{
//...其余方法同上,注意catch方法
finally(onFinally) {
this.then(() => onFinally(), () => onFinally())
}
}
const promise = new myPromise((resolve, reject) => {
console.log('执行了'); // 执行了
resolve('res')
}).then((res) => {
console.log("res:", res) // res: res
}).catch((err) => {
console.log("err:", err)
}).finally(() => {
console.log("finally") // finally
})
Promise.resolve && Promise.reject方法直接实现
通过设置静态函数来 new 一个新的 promise 就可以了:
class myPromise{
//...其余方法同上
static resolve(value) {
return new myPromise((resolve, reject) => {
resolve(value)
})
}
static reject(reason) {
return new myPromise((resolve, reject) => {
reject(reason)
})
}
}
myPromise.resolve('Hello World').then(res => {
console.log("res:", res) // res: Hello World
})
myPromise.reject('err').catch(err => {
console.log("err:", err) // err: err
})
Promise.all && Promise.allSettled方法实现
两个方法都是通过对数组进行遍历,分别执行Promise,但不同的是:Promise.allSettled返回的是一个数组,数组中的每一项是一个Promise的状态,而Promise.all返回的是一个Promise,Promise.all返回的Promise的状态是数组中所有Promise的状态的并集。
class myPromise {
// ...其余方法同上
static all(promises) {
return new myPromise((resolve, reject) => {
let result = []
promises.forEach(promise => {
promise.then(
res => {
result.push(res)
// 如果数组中的所有Promise都执行完毕,则执行resolve
if (result.length === promises.length) {
resolve(result)
}
}),
// 如果数组中的某一个Promise执行失败,则执行reject
err => {
reject(err)
})
})
})
}
static allSettled(promises) {
return new myPromise((resolve, reject) => {
let result = []
promises.forEach(promise => {
// 对每个结果进行分类,分为fulfilled和rejected,加入到数组中
promise.then(
res => {
result.push({status: 'fulfilled', value: res})
if (result.length === promises.length) {
resolve(result)
}
}),
err => {
result.push({status: 'rejected', reason: err})
if (result.length === promises.length) {
resolve(result)
}
})
})
})
}
}
const p1 = new myPromise((resolve) => {
setTimeout(() => { resolve(1111) }, 1000)
})
const p2 = new myPromise((resolve, reject) => {
setTimeout(() => { reject(2222) }, 2000)
})
const p3 = new myPromise((resolve) => {
setTimeout(() => { resolve(3333) }, 3000)
})
myPromise.all([p1, p2, p3]).then(res => {
console.log("res:", res) // res: [ 1111, 2222, 3333 ]
})
myPromise.allSettled([p1, p2, p3]).then(res => {
console.log("res:", res) // res: [ { status: 'fulfilled', value: 1111 }, { status: 'rejected', reason: 2222 }, { status: 'fulfilled', value: 3333 } ]
})
Promise.race && Promise.any 方法设计
两个方法都是直接执行第一个可以执行Promise,区别在于:race执行到第一个就可以返回,any则需要等待一个resolve才可以返回,否则返回reject:
class myPromise {
// ...同上
static race(promises) {
return new myPromise((resolve, reject) => {
promises.forEach(promise => {
// promise.then(res => {
// resolve(res)
// }, err => {
// reject(err)
// })
promise.then(resolve, reject)
})
})
}
static any(promises) {
// resolve必须等到有一个成功的结果
// reject所有的都失败才执行reject
const reasons = []
return new myPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, err => {
// 如果有一个Promise执行成功,则执行resolve
reasons.push(err)
if (reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
})
})
})
}
}
const p1 = new myPromise((resolve) => {
setTimeout(() => { resolve(1111) }, 1000)
})
const p2 = new myPromise((resolve, reject) => {
setTimeout(() => { reject(2222) }, 2000)
})
const p3 = new myPromise((resolve) => {
setTimeout(() => { resolve(3333) }, 3000)
}).
myPromise.race([p1, p2, p3]).then(res => {
console.log(res); // 111
})
myPromise.any([p1, p2, p3]).then(res => {
console.log(res); // 111
})