1. 手写 Promise
1.1 实现 MYPromise
- 正常的 Promise 是可以传入两个回调函数的:一个成功回调,一个失败回调,所以我们实现的这个类应该是包含这两个回调的;
- resolve 和 reject 函数二者是互斥的,即:执行完一个之后,另外一个是不执行的,所以我们需要使用状态来记录二者回调时的执行状态;
- 此外,函数是可以执行 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) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
console.log('resolve')
}
const reject = reason => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
console.log('reject')
}
executor(resolve, reject)
}
}
const promise = new MYPromise((resolve, reject) => {
resolve(111)
reject(222)
})
promise.then(
res => {},
err => {}
)
1.2 基础版 then 方法
- setTimeout
- then 方法也是接收两个回调函数的,一个成功回调,一个失败回调。所以我们使用两个变量来记录这两个回调,使用下述代码时会出现异常,就是在 resolve 或者 reject 时提示 this.onFulfilled 或者 this.onRejected 是 undefined 无法执行函数操作,因此我们首先考虑的是将其加入宏任务队列中。
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.onFulfilled = undefined
this.onRejected = undefined
const resolve = value => {
// setTimeout(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
console.log('resolve')
this.onFulfilled(this.value)
// }, 0)
}
const reject = reason => {
// setTimeout(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
console.log('reject')
this.onRejected(this.reason)
// }, 0)
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
}
const promise = new MYPromise((resolve, reject) => {
reject(222)
resolve(111)
})
promise.then(
res => {
console.log('res: ', res)
},
err => {
console.log('err: ', err)
}
)
- queueMicrotash
使用如下代码可以正确地执行 then 方法了,但是会存在两个问题:
-
- 无法多次执行 then 方法;
-
- 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
this.onFulfilled = undefined
this.onRejected = undefined
const resolve = value => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
queueMicrotask(() => {
this.value = value
this.onFulfilled(this.value)
})
}
const reject = reason => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
queueMicrotask(() => {
this.reason = reason
this.onRejected(this.reason)
})
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
}
const promise = new MYPromise((resolve, reject) => {
resolve(111)
reject(222)
})
promise.then(
res => {
console.log('res: ', res)
},
err => {
console.log('err: ', err)
}
)
/**
* 这份代码存在的几个问题:
* 1. 无法多次执行 then 方法
* 2. then 方法不能链式调用
*/
promise
.then(
res => {
console.log('res1: ', res)
},
err => {
console.log('err1: ', err)
}
)
.then(() => {})
1.3 then 方法多次执行
为保证 then 方法多次执行,我们需要将上述的 this.onFulfilled 和 this.onRejected 定义为函数。
- 执行 then 方法时,如果状态已经确定,我们只需执行正确地回调即可;
- 如果状态还未确定,我们需要将需要执行的回调函数加入到 this.onFulfilled 或 this.onRejected 的函数中,然后在 resolve 或者 reject 中依次遍历方法即可。
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 => {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.map(fn => fn(this.value))
})
}
const reject = reason => {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.map(fn => fn(this.reason))
})
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
// 1. 如果在 then 调用的时候,状态已经确定 --> 为了解决 setTimeout 延时中的 then 回调
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
onFulfilled(this.value)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
onRejected(this.reason)
}
// 2. 将成功和失败的回掉函数放入数组中
if (this.status === PROMISE_STATUS_PENDING) {
if (onFulfilled instanceof Function) {
this.onFulfilledFns.push(onFulfilled)
}
if (onRejected instanceof Function) {
this.onRejectedFns.push(onRejected)
}
}
}
}
const promise = new MYPromise((resolve, reject) => {
resolve(111)
reject(222)
})
promise.then(
res => {
console.log('res1: ', res)
},
err => {
console.log('err1: ', err)
}
)
promise.then(
res => {
console.log('res2: ', res)
},
err => {
console.log('err2: ', err)
}
)
setTimeout(() => {
promise.then(res => {
console.log('res3: ', res)
})
}, 1000)
1.4 then 方法链式调用
为保证 then 方法可以链式调用,我们需要在 then 函数中返回我们自定义的 MYPromise 方法,并且在执行完回调后拿到值并使用 resolve 或者 reject 返回对应的值。
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 => {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.map(fn => fn(this.value))
})
}
const reject = reason => {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.map(fn => fn(this.reason))
})
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
return new MYPromise((resolve, reject) => {
// 1. 如果在 then 调用的时候,状态已经确定 --> 为了解决 setTimeout 延时中的 then 回调
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
try {
const value = onFulfilled(this.value)
resolve(value)
} catch (err) {
reject(err)
}
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch (err) {
reject(err)
}
}
// 2. 将成功和失败的回掉函数放入数组中
if (this.status === PROMISE_STATUS_PENDING) {
if (onFulfilled instanceof Function) {
// 如果直接将回掉函数放入到数组中,那么我们无法拿到回掉函数的执行结果,因此无法做到链式调用,于是考虑在外层包裹一层回掉函数
// this.onFulfilledFns.push(onFulfilled)
this.onFulfilledFns.push(() => {
try {
const value = onFulfilled(this.value)
resolve(value)
} catch (err) {
reject(err)
}
})
}
if (onRejected instanceof Function) {
this.onRejectedFns.push(() => {
try {
const reason = onRejected(this.reason)
resolve(reason)
} catch (err) {
reject(err)
}
})
}
}
})
}
}
const promise = new MYPromise((resolve, reject) => {
// resolve(111)
// reject(222)
throw new Error('err message')
})
promise
.then(
res => {
console.log('res1: ', res)
// return 'aaaa'
throw new Error('err message')
},
err => {
console.log('err1: ', err)
// return 'bbbb'
throw new Error('err message')
}
)
.then(
res => {
console.log('res2: ', res)
},
err => {
console.log('err2: ', err)
}
)
由于 try catch 在此代码中多次出现,我们可以考虑将其封装为一个函数:
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
function execFunctionWithCatchError(execFunc, value, resolve, reject) {
try {
const result = execFunc(value)
resolve(result)
} catch (err) {
reject(err)
}
}
class MYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = value => {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.map(fn => fn(this.value))
})
}
const reject = reason => {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.map(fn => fn(this.reason))
})
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
return new MYPromise((resolve, reject) => {
// 1. 如果在 then 调用的时候,状态已经确定 --> 为了解决 setTimeout 延时中的 then 回调
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2. 将成功和失败的回掉函数放入数组中
if (this.status === PROMISE_STATUS_PENDING) {
if (onFulfilled instanceof Function) {
// 如果直接将回掉函数放入到数组中,那么我们无法拿到回掉函数的执行结果,因此无法做到链式调用,于是考虑在外层包裹一层回掉函数
// this.onFulfilledFns.push(onFulfilled)
this.onFulfilledFns.push(() => {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
}
if (onRejected instanceof Function) {
this.onRejectedFns.push(() => {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
}
})
}
}
const promise = new MYPromise((resolve, reject) => {
// resolve(111)
reject(222)
// throw new Error('err message')
})
promise
.then(
res => {
console.log('res1: ', res)
return 'aaaa'
// throw new Error('err message')
},
err => {
console.log('err1: ', err)
return 'bbbb'
// throw new Error('err message')
}
)
.then(
res => {
console.log('res2: ', res)
},
err => {
console.log('err2: ', err)
}
)
1.5 catch 方法
直接调用写好的 then 方法即可,第一个参数为 undefined;然后在 then 方法中对 onRejected 方法进行改写一下即可。
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
function execFunctionWithCatchError(execFunc, value, resolve, reject) {
try {
const result = execFunc(value)
resolve(result)
} catch (err) {
reject(err)
}
}
class MYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = value => {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.map(fn => fn(this.value))
})
}
const reject = reason => {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.map(fn => fn(this.reason))
})
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
onRejected = onRejected || (err => reject(err))
return new MYPromise((resolve, reject) => {
// 1. 如果在 then 调用的时候,状态已经确定 --> 为了解决 setTimeout 延时中的 then 回调
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2. 将成功和失败的回掉函数放入数组中
if (this.status === PROMISE_STATUS_PENDING) {
if (onFulfilled instanceof Function) {
// 如果直接将回掉函数放入到数组中,那么我们无法拿到回掉函数的执行结果,因此无法做到链式调用,于是考虑在外层包裹一层回掉函数
// this.onFulfilledFns.push(onFulfilled)
this.onFulfilledFns.push(() => {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
}
if (onRejected instanceof Function) {
this.onRejectedFns.push(() => {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
}
})
}
catch(onRejected) {
// 这样一来,我们在 catch 中执行的回调相当于是第一个 then 方法的返回函数,
// 但是由于第一层 catch 是 undefined,所以我们需要再 then 方法中重新定义 onRejected 方法。
this.then(undefined, onRejected)
}
}
const promise = new MYPromise((resolve, reject) => {
// resolve(111)
reject(222)
// throw new Error('err message')
})
promise
.then(res => {
console.log('res1: ', res)
})
.catch(err => {
console.log('err1: ', err)
})
1.6 finally 方法
- 由于我们可能需要再 finally 方法调用对应的 then 方法返回的值,所以我们需要将 onFulfilled 值进行改写;
- catch 也需要返回一个 MYPromise
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
function execFunctionWithCatchError(execFunc, value, resolve, reject) {
try {
const result = execFunc(value)
resolve(result)
} catch (err) {
reject(err)
}
}
class MYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = value => {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.map(fn => fn(this.value))
})
}
const reject = reason => {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.map(fn => fn(this.reason))
})
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
onRejected =
onRejected ||
(err => {
throw err
})
onFulfilled =
onFulfilled ||
(value => {
return value
})
return new MYPromise((resolve, reject) => {
// 1. 如果在 then 调用的时候,状态已经确定 --> 为了解决 setTimeout 延时中的 then 回调
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2. 将成功和失败的回掉函数放入数组中
if (this.status === PROMISE_STATUS_PENDING) {
if (onFulfilled instanceof Function) {
// 如果直接将回掉函数放入到数组中,那么我们无法拿到回掉函数的执行结果,因此无法做到链式调用,于是考虑在外层包裹一层回掉函数
// this.onFulfilledFns.push(onFulfilled)
this.onFulfilledFns.push(() => {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
}
if (onRejected instanceof Function) {
this.onRejectedFns.push(() => {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
}
})
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(onFinally) {
this.then(
() => {
onFinally()
},
() => {
onFinally()
}
)
}
}
const promise = new MYPromise((resolve, reject) => {
resolve(111)
// reject(222)
// throw new Error('err message')
})
promise
.then(res => {
console.log('res1: ', res)
return 'aaa'
})
.then(res => {
console.log('res2: ', res)
})
.catch(err => {
console.log('err1: ', err)
})
.finally(() => {
console.log('finally')
})
1.7 其他类方法(resolve、reject、all、allSettled、race、any)
const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'
function execFunctionWithCatchError(execFunc, value, resolve, reject) {
try {
const result = execFunc(value)
resolve(result)
} catch (err) {
reject(err)
}
}
class MYPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = value => {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_FULFILLED
this.value = value
this.onFulfilledFns.map(fn => fn(this.value))
})
}
const reject = reason => {
queueMicrotask(() => {
if (this.status !== PROMISE_STATUS_PENDING) return
this.status = PROMISE_STATUS_REJECTED
this.reason = reason
this.onRejectedFns.map(fn => fn(this.reason))
})
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
onRejected =
onRejected ||
(err => {
throw err
})
onFulfilled =
onFulfilled ||
(value => {
return value
})
return new MYPromise((resolve, reject) => {
// 1. 如果在 then 调用的时候,状态已经确定 --> 为了解决 setTimeout 延时中的 then 回调
if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2. 将成功和失败的回掉函数放入数组中
if (this.status === PROMISE_STATUS_PENDING) {
if (onFulfilled instanceof Function) {
// 如果直接将回掉函数放入到数组中,那么我们无法拿到回掉函数的执行结果,因此无法做到链式调用,于是考虑在外层包裹一层回掉函数
// this.onFulfilledFns.push(onFulfilled)
this.onFulfilledFns.push(() => {
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
}
if (onRejected instanceof Function) {
this.onRejectedFns.push(() => {
execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
}
})
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(onFinally) {
this.then(
() => {
onFinally()
},
() => {
onFinally()
}
)
}
static resolve(value) {
return new MYPromise(resolve => resolve(value))
}
static reject(reason) {
return new MYPromise((resolve, reject) => reject(reason))
}
static all(promises) {
return new MYPromise((resolve, reject) => {
const values = []
promises.map(promise => {
promise
.then(res => {
values.push(res)
if (values.length === promises.length) {
resolve(values)
}
})
.catch(err => {
reject(err)
})
})
})
}
static allSettled(promises) {
const values = []
return new MYPromise(resolve => {
promises.map(promise => {
promise
.then(res => {
values.push({ status: PROMISE_STATUS_FULFILLED, value: res })
if (values.length === promises.length) {
resolve(values)
}
})
.catch(err => {
values.push({ status: PROMISE_STATUS_REJECTED, value: err })
if (values.length === promises.length) {
resolve(values)
}
})
})
})
}
static race(promises) {
return new MYPromise((resolve, reject) => {
promises.map(promise => {
promise.then(resolve, reject)
})
})
}
static any(promises) {
// resolve 必须等到有一个成功的结果
// reject 所有的都失败才执行 reject
const reasons = []
return new MYPromise((resolve, reject) => {
promises.map(promise => {
promise.then(resolve, err => {
reasons.push(err)
if (reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
})
})
})
}
}
const promise = new MYPromise((resolve, reject) => {
resolve(111)
// reject(222)
// throw new Error('err message')
})
promise
.then(res => {
console.log('res1: ', res)
return 'aaa'
})
.then(res => {
console.log('res2: ', res)
})
.catch(err => {
console.log('err1: ', err)
})
.finally(() => {
console.log('finally')
})
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(111)
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(222)
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(333)
}, 3000)
})
MYPromise.all([p1, p2, p3])
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
MYPromise.allSettled([p1, p2, p3])
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
MYPromise.race([p1, p2, p3])
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
MYPromise.any([p1, p2, p3])
.then(res => {
console.log(res)
})
.catch(err => {
console.log('errs:', err.errors)
})