手写 Promise
// Promise 构造函数参数是一个可执行函数 executor,有两个参数 resolve 和 rejected(可选)
// 有至少以下属性 status (Promise 状态)、value(提供给 onFulfilled 回调函数的参数,等于就是成功的值)、reason(失败的值)
// 在 constructor 中执行 executor(resolve, reject),需要 try catch 包裹,catch 中调用 reject
// resolve 更改状态未 Fulfilled, reject 更改状态为 Rejected
// then 方法也有两个回调函数参数 onFulfilled 和 onRejected(但是不一定为函数),返回的是一个 Promise
// 由于异步任务执行结束时机并不知道,所以先将回调函数(onFulfilled, onRejected)和返回的promise的 resolve 和 reject 方法存起来为一个 handler 数组。(简单理解为resolve 和 reject 处理返回的这个 promise 的状态),等待 Promise 的状态变更后再执行对应的回调函数。
// 如果 Promise 是一个已完成的状态(处理同步任务),那么就直接去执行 onFulfilled 或者 onRejected 回调函数。
// 当 executor 完成后,执行 resolve 或者 reject 方法时,更改 Promise 的状态,并且赋值 value 或者 status,然后触发回调函数的执行,从 handler 数组中取出 onFulfilled, onRejected, resolve, reject。
//(这部分代码都需要在微任务队列中执行,比如放在 queueMicrotask 中)根据 promise 的状态 status 来决定执行 onFulfilled 还是 onRejected。如果它们是函数,那么就在 try catch 中执行它们,得到的结果通过之前新的 promise 的 resolve 方法处理这个新 promise 的状态, 失败就 reject(这样就实现了链式调用)。如果不是函数,那么就根据当前 promise 的状态来决定调用 resolve 还是 reject。如果是函数并且是一个 promise(是个对象并且有then方法)的话,就调用then方法(data.then(resolve, reject))
// catch 方法就是 then 方法,第一个参数未空
// all:返回一个新的 promise,循环执行 promise(不是的话就Promise.resolve为一个promise),then 中将结果根据索引位置存起来,并计数 + 1,如果计数等于 promises 长度,就 resolve,失败就直接 reject
// allSettled:分别在每个 promise 的then方法和catch方法中返回对应的状态和值(等于指定了回调函数)。
enum PromiseStatus {
Pending = 'pending',
Fulfilled = 'fulfilled',
Rejected = 'rejected'
}
type CallbackFn = (value: unknown) => void
type FulfilledFn<T> = (value: T) => void
type PromiseHandlers<T> = {
onFulfilled: FulfilledFn<T>
onRejected: CallbackFn
resolve: CallbackFn
reject: CallbackFn
}
type PromiseExecutor = (resolve: CallbackFn, reject?: CallbackFn) => void
class MyPromise<T> {
private value: T = undefined
private reason: unknown = undefined
private status: PromiseStatus = PromiseStatus.Pending
private handlers: PromiseHandlers<T>[] = []
constructor(executor: PromiseExecutor) {
const resolve = (value: T) => {
if (this.status === PromiseStatus.Pending) {
this.status = PromiseStatus.Fulfilled
this.value = value
this.runHandlers()
}
}
const reject = (reason: unknown) => {
if (this.status === PromiseStatus.Pending) {
this.status = PromiseStatus.Rejected
this.reason = reason
this.runHandlers()
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
static isPromiseLike(value) {
return value && typeof value === 'object' && typeof value.then === 'function'
}
private runCallback(callback, resolve, reject) {
queueMicrotask(() => {
const result = this.status === PromiseStatus.Fulfilled ? this.value : this.reason
if (typeof callback === 'function') {
try {
const data = callback(result)
if (MyPromise.isPromiseLike(data)) {
data.then(resolve, reject)
} else {
resolve(data)
}
} catch (error) {
reject(error)
}
} else {
if (this.status === PromiseStatus.Fulfilled) {
resolve(this.value)
} else {
reject(this.reason)
}
}
})
}
private runHandlers() {
if (this.status === PromiseStatus.Pending) {
return
}
while(this.handlers.length > 0) {
const { onFulfilled, onRejected, resolve, reject} = this.handlers.shift()
if (this.status === PromiseStatus.Fulfilled) {
this.runCallback(onFulfilled, resolve, reject)
} else {
this.runCallback(onRejected, resolve, reject)
}
}
}
then(onFulfilled: FulfilledFn<T>, onRejected?) {
return new MyPromise((resolve, reject) => {
this.handlers.push({
onFulfilled,
onRejected,
resolve,
reject
})
this.runHandlers()
})
}
catch(onRejected) {
return this.then(null, onRejected)
}
static resolve(value: unknown) {
return new MyPromise((resolve) => {
resolve(value)
})
}
static reject(value: unknown) {
return new MyPromise((_, reject) => {
reject(value)
})
}
static all(promises: MyPromise<any>[]) {
return new MyPromise((resolve, reject) => {
const result = []
let count = 0
promises.forEach((promise, index) => {
promise.then((res) => {
result[index] = res
count ++
if (count === promises.length) {
resolve(result)
}
}, reject)
})
})
}
static race(promises: any[]) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
if (!MyPromise.isPromiseLike(promise)) {
resolve(promise)
}
promise.then(resolve, reject)
})
})
}
static allSettled(promises: any[]) {
return MyPromise.all(promises.map((item) => {
return item.then((res) => {
return {
status: 'fulfilled',
value: res
}
}).catch((err) => {
return {
status: 'rejected',
reason: err
}
})
}))
}
}
常见面试题