完整代码
先上代码,后续再逐个完成。
const STATE = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected'
}
class MyPromise {
#state = STATE.PENDING
#result = undefined
#handlers = []
#resolve = (value) => {
this.#changeState(STATE.FULFILLED, value)
}
#reject = (reason) => {
this.#changeState(STATE.REJECTED, reason)
}
constructor(callback) {
try {
callback(this.#resolve, this.#reject)
} catch (error) {
reject(error)
}
}
#changeState(state, data) {
if (this.#state !== STATE.PENDING) {
return
}
this.#state = state
this.#result = data
this.#runAll()
}
#runAll() {
if (this.#state === STATE.PENDING) {
return
}
while (this.#handlers.length) {
this.#runOne(this.#handlers.shift())
}
}
#runOne({ onFulfilled, onRejected, resolve, reject }) {
queueMicrotask(() => {
const isFulfilled = this.#state === STATE.FULFILLED
const callback = isFulfilled ? onFulfilled : onRejected
const settled = isFulfilled ? resolve : reject
if (typeof callback !== 'function') {
settled(this.#result)
return
}
try {
const data = callback(this.#result)
if (this.#isPromiseLike(data)) {
data.then(resolve, reject)
} else {
resolve(data)
}
} catch (error) {
reject(error)
}
})
}
#isPromiseLike(value) {
if (value === null) {
return false
}
const valueType = typeof value
if (valueType !== 'function' && valueType !== 'object') {
return false
}
if (typeof value.then !== 'function') {
return false
}
return true
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
this.#handlers.push({
onFulfilled,
onRejected,
resolve,
reject
})
this.#runAll()
})
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(onFinally) {
return this.then(
(data) => {
onFinally()
return data
},
(reason) => {
onFinally()
return reason
}
)
}
static #instance
static {
this.#instance = new this((res) => {
res()
})
}
static #each(promises, onPromise, onOther) {
for (let i = 0; i < promises.length; i++) {
const promise = promises[i]
if (this.#instance.#isPromiseLike(promise)) {
onPromise(promise, i)
} else {
onOther(promise, i)
}
}
}
static #useValue(len, settled) {
const arr = []
let arrRealLen = 0
const isComplete = () => {
if (arrRealLen !== len) {
return
}
settled(arr)
}
const addValue = (value, i) => {
arr[i] = value
arrRealLen += 1
isComplete()
}
return addValue
}
static resolve(value) {
if (value instanceof this) {
return value
}
return new this((resolve, reject) => {
if (this.#instance.#isPromiseLike(value)) {
value.then(resolve, reject)
} else {
resolve(value)
}
})
}
static reject(reason) {
return new this((_, reject) => {
reject(reason)
})
}
static all(promises) {
return new this((resolve, reject) => {
const addValue = this.#useValue(promises.length, resolve)
const onPromise = (promise, i) =>
promise.then(
(data) => {
addValue(data, i)
},
(reason) => {
reject(reason)
}
)
this.#each(promises, onPromise, addValue)
})
}
static race(promises) {
return new this((resolve, reject) => {
const onPromise = (promise) => {
promise.then(resolve, reject)
}
this.#each(promises, onPromise, resolve)
})
}
static allSettled(promises) {
return new this((resolve) => {
const addValue = this.#useValue(promises.length, resolve)
const onPromise = (promise, i) => {
promise.then(
(value) => {
addValue({ status: STATE.FULFILLED, value }, i)
},
(reason) => {
addValue({ status: STATE.REJECTED, reason }, i)
}
)
}
const onOther = (value, i) => {
addValue({ status: STATE.FULFILLED, value }, i)
}
this.#each(promises, onPromise, onOther)
})
}
static any(promises) {
return new this((resolve, reject) => {
const addValue = this.#useValue(promises.length, reject)
const onPromise = (promise, i) =>
promise.then(
(data) => {
resolve(data)
},
(reason) => {
addValue(reason, i)
}
)
this.#each(promises, onPromise, resolve)
})
}
}
构造函数
- Promise 有种状态:pending(进行中)、 fulfilled( 已成功)、rejected(已失败),并且状态一旦发生改变就不会再变。
- Promise 的构造函数接收一个函数为参数,这个函数的有两个参数,分别是 resolve和 reject。resolve的作用是将 promise的状态从 pending转为 fulfilled, reject的作用是将 promise的状态从 pending转为 rejected。
- 构造函数接收的函数参数报错时,会将 promise的状态从 pending转为 rejected。
// 定义Promise的状态
const STATE = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected'
}
class MyPromise {
#state = STATE.PENDING, // promise的状态
#result = undefined, // promise的结果
// 由于 this指向问题,需要定义成箭头函数
#resolve = (value) => {
this.#changeState(STATE.FULFILLED, value)
}
#reject = (reason) => {
this.#changeState(STATE.REJECTED, reason)
}
constructor(executor) {
try {
// 将 resolve和 reject传给 Promise的函数
executor(this.#resolve, this.#reject)
} catch (error) {
// executor 报错时直接将状态转为 rejected
reject(error)
}
}
// 修改 promise的状态
#changeState(state, data) {
// 状态改变就不再发生变化
if (this.#state !== STATE.PENDING) {
return
}
this.#state = state
this.#result = data
}
}
then
-
then 接收两个函数作为参数,第一个参数是在 promise状态为 fulfilled的时候执行,第二个参数时在 rejected的时候执行。
-
then 返回一个新的 Promise实例。
class MyPromise {
#state = STATE.PENDING,
#result = undefined,
#handlers = [] // 收集 then的参数和 then返回的promise的 resolve和 reject
// ...
#changeState(state, data) {
if (this.#state !== STATE.PENDING) {
return
}
this.#state = state
this.#result = data
this.#runAll() // 调用一次,此时 promise状态已改变,如果 #handlers收集了函数,则可以执行
}
#runAll() {
// 如果是 pending状态就不执行
if (this.#state === STATE.PENDING) {
return
}
// 取出数组的第一项进行执行,直到数组为空
while (this.#handlers.length) {
this.#runOne(this.#handlers.shift())
}
}
#runOne({ onFulfilled, onRejected, resolve, reject }) {
// 执行一项 ...
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
// 由于此时不知道 promise的状态,且一个 promise实例可以多次调用 then,所以先用数组将函数收集,等待执行
this.#handlers.push({
onFulfilled,
onRejected,
resolve,
reject
})
this.runAll() // 调用一次,此时函数已收集,如果 promise状态已改变,则可以执行
})
}
}
- then 的参数放入微任务队列等待执行,如果传入的不是函数,则会将上次的结果透传给下一个 then,如果是函数就调用,执行时报错则直接返回失败的 promise,不报错则拿到函数的返回值,如果返回值是一个 thenable对象(具有then方法的对象),则调用它的 then方法,并将 resolve和 reject作为参数传给它,否则调用 resolve方法。
#runOne({ onFulfilled, onRejected, resolve, reject }) {
// 创建异步任务
queueMicrotask(() => {
// 根据 promise的状态选择对应的函数
const isFulfilled = this.#state === STATE.FULFILLED
const callback = isFulfilled ? onFulfilled : onRejected
const settled = isFulfilled ? resolve : reject
// 如果不是函数,则透传给下一个then
if (typeof callback !== 'function') {
settled(this.#result)
return
}
try {
// 将上一个promise的结果传给 then的回调,并拿到回调的返回值
const data = callback(this.#result)
// 判断是不是 thenable对象
if (this.#isPromiseLike(data)) {
data.then(resolve, reject)
} else {
resolve(data)
}
} catch (error) {
reject(error)
}
})
}
// 判断是不是 thenable对象
#isPromiseLike(value) {
// 如果为 null,则不是 thenable对象 (typeof null === 'object')
if (value === null) {
return false
}
const valueType = typeof value
// 如果既不是函数也不是对象,则不是 thenable对象
if (valueType !== 'function' && valueType !== 'object') {
return false
}
// 如果它的 then方法不是函数,则不是 thenable对象
if (typeof value.then !== 'function') {
return false
}
// 否则为 thenable对象
return true
}
catch
- catch 用于捕获前面所抛出的错误,接收一个函数作为参数,作用等同于 then的第二个参数。
- catch 返回一个新的 Promise实例。
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally
- 不管 Promise 对象最后状态如何,都会执行的 finally里面的函数。
- finally 的回调函数不接受任何参数。
- finally 返回一个新的 Promise实例,并且值与它的上一个promise相同。
finally(onFinally) {
return this.then(
(data) => {
onFinally() // 运行回调
return data // 将上一个promise传过来的参数返回,透传给下一个promise
},
(reason) => {
onFinally()
return reason
}
)
}
resolve
作用:将数据转成一个promise对象
- 参数是一个 Promise实例时,直接返回这个实例。
- 参数时一个 thenable对象时,返回一个新的 Promise实例,并将这个实例的 resolve和 reject传给这个对象的 then方法。
- 参数时其他数据时,返回一个新的 Promise实例,并将这个参数作为实例的结果。
// 定义一个实例,方便再静态方法中调用实例方法
static #instance
// 静态块 在类生成时运行,且只运行一次,对静态属性赋值
static {
// 静态方法里面的 this指向构造函数
this.#instance = new this((res) => {
res()
})
}
static resolve(value) {
// 参数时 Promise实例
if (value instanceof this) {
return value
}
return new this((resolve, reject) => {
// 参数是 thenable
if (this.#instance.#isPromiseLike(value)) {
value.then(resolve, reject)
} else {
resolve(value)
}
})
}
reject
作用:返回一个新的 Promise实例,该实例的状态为 rejected。
static reject(reason) {
return new this((_, reject) => {
reject(reason)
})
}
all
- 接收有个数组作为参数,数组的每一个都是 Promise实例,如果不是,则将其转为成功的 promise。
- 返回一个新的 Promise实例。
- 参数数组中所有 promise都为 fulfilled状态时,返回的 Promise实例状态也为 fulfilled,否则为 rejected。
由于 all、race、allSettled、any都需要对参数数组循环判断每一项是否为 Promise实例,所以先创建函数用来判断并根据不同情况处理。
// 接收三个参数,第一个是 Promise实例数组,第二个是数组项为 Promise实例的回调,第三个是数组项不是 Promise实例的回调。
static #each(promises, onPromise, onOther) {
for (let i = 0; i < promises.length; i++) {
const promise = promises[i]
// 判断是否为 promise
if (this.#instance.#isPromiseLike(promise)) {
onPromise(promise, i)
} else {
onOther(promise, i)
}
}
}
all、allSettled、any都会返回结果为数组的 Promise实例,所以可以创建函数实现复用。
// 第一个参数是 promise数组的长度,第二个是 resolve或 reject,根据需求传,用来改变返回的 Promise实例的状态
static #useValue(len, settled) {
const arr = [] // 收集状态改变的 promise,并作为结果返回
let arrRealLen = 0 // 已收集的个数 (不能保证数组中的 promise是按顺序完成的,所以不能使用 arr.length)
// 如果 promise状态全部改变,则改变返回的 Promise实例的状态
const isComplete = () => {
if (arrRealLen !== len) {
return
}
settled(arr)
}
// 数组项的 promise状态改变后添加到数组,并判断是否全部收集完成
const addValue = (value, i) => {
arr[i] = value
arrRealLen += 1
isComplete()
}
return addValue
}
工具函数完成后,接下来就是 Promise.all了
static all(promises) {
// 返回一个新的 Promise实例
return new this((resolve, reject) => {
// 将 resolve传给工具函数,当收集完成时自动调用,并获取向数组添加值的方法
const addValue = this.#useValue(promises.length, resolve)
const onPromise = (promise, i) =>
promise.then(
// 当前 promise状态为 fulfilled时,将结果添加到数组
(data) => {
addValue(data, i)
},
// 当前 promise状态为 rejected时,直接改变调用 reject改变返回的Promise实例的状态
(reason) => {
reject(reason)
}
)
// 对参数数组项进行处理
this.#each(promises, onPromise, addValue)
})
}
race
- 接收一个数组作为参数,且返回一个新的 Promise实例,状态和数组中第一个状态改变的 promise相同。
static race(promises) {
return new this((resolve, reject) => {
// 如果是 promise,则根据它的状态调用 resolve或 reject
const onPromise = (promise) => {
promise.then(resolve, reject)
}
// 如果不是 promise,直接调用 resolve
this.#each(promises, onPromise, resolve)
})
}
allSettled
- 当参数数组中所有 promise状态改变时,返回一个状态为 fulfilled的Promise实例
static allSettled(promises) {
return new this((resolve) => {
const addValue = this.#useValue(promises.length, resolve)
const onPromise = (promise, i) => {
// 不管 promise状态时 fulfilled还是 rejected,都向数组中添加
promise.then(
(value) => {
addValue({ status: STATE.FULFILLED, value }, i)
},
(reason) => {
addValue({ status: STATE.REJECTED, reason }, i)
}
)
}
const onOther = (value, i) => {
addValue({ status: STATE.FULFILLED, value }, i)
}
this.#each(promises, onPromise, onOther)
})
}
any
- any与all相反,参数数组中所有 promise都为 rejected状态时,返回的 Promise实例状态也为 rejected,否则为 fulfilled。
static any(promises) {
return new this((resolve, reject) => {
const addValue = this.#useValue(promises.length, reject)
const onPromise = (promise, i) =>
promise.then(
// 当前 promise状态为 fulfilled时,直接改变调用 resolve
(data) => {
resolve(data)
},
// 当前 promise状态为 rejected时,将结果添加到数组
(reason) => {
addValue(reason, i)
}
)
this.#each(promises, onPromise, resolve)
})
}
总结
- 类中可以用 # 定义私有属性和方法。
- 静态方法中的 this指向构造函数,实例方法中的 this指向实例。
- 如果想在静态方法中调用实例方法,可以定义一个静态属性接收实例(单例模式),并在静态块在给它赋值(只会运行一次)。
- queueMicrotask可以将任务添加到微任务队列,也可以使用 process.nextTick(node环境)、MutationObserver(浏览器环境)。Vue.nextTick将任务加到微队列的方法优先级分别是:Promise > MutationObserver > setImmediate > setTimeout。