在现代的 JavaScript 开发中,Promise 是处理异步操作的一种强大工具。本文将带你从零开始,手把手地实现一个简单的Promise,让你更深入地理解 Promise 的工作原理和实现机制。
首先什么是Promise?
在Promise A+规范里,我们可以关注这一句话““promise” is an object or function with a then method whose behavior conforms to this specification.”,这句话是重点,我们后面需要用得到。
Promise 的基本结构
我们首先来定义 Promise 的基本结构。一个 Promise 实例具有以下特性:
- 三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),一旦 Promise 进入了 fulfilled 或 rejected 状态,它就是 settled(已定型)的,不会再改变状态。
- 一个结果值,可以是任意类型的数据
- 可以注册回调函数,处理异步操作的结果
话不多说,让我们直接上完整代码。
class MyPromise {
// 定义状态常量
#PENDING = 'PENDING'
#FULFILLED = 'FULFILLED'
#REJECTED = 'REJECTED'
// 定义初始化状态
#state = this.#PENDING
// 保存返回值
#result = undefined
// 因为then可以链式调用,所以这里设置数组保存数据
#handles = []
constructor(executor) {
const resolve = (value) => {
this.#changeState(this.#FULFILLED, value)
}
const reject = (reason) => {
this.#changeState(this.#REJECTED, reason)
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
// 当状态改变的时候调用
#changeState(state, result) {
if (this.#state !== this.#PENDING) return
this.#state = state
this.#result = result
this.#run()
}
// 判断是否是promise like
#isPromiseLike(value) {
return value !== null && (typeof value === 'object' || typeof value === 'function') && typeof value.then === 'function'
}
// 把任务添加到微队列中
#runMicroTask(func) {
queueMicrotask(func)
}
// 抽取公共代码
#runOnce(callback, resolve, reject) {
this.#runMicroTask(() => {
if(typeof callback === 'function') {
try {
const result = callback(this.#result)
if(this.#isPromiseLike(result)) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
} else {
const fn = this.#state === this.#FULFILLED ? resolve : reject
fn(this.#result)
}
})
}
// 执行then
#run() {
if (this.#state === this.#PENDING) return
while (this.#handles.length) {
const { onFulfilled, onRejected, resolve, reject } = this.#handles.shift()
if(this.#state === this.#FULFILLED) {
this.#runOnce(onFulfilled, resolve, reject)
} else if(this.#state === this.#REJECTED) {
this.#runOnce(onRejected, resolve, reject)
}
}
}
// then方法用于注册成功和失败的回调函数
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this.#handles.push({
onFulfilled,
onRejected,
resolve,
reject
})
this.#run()
})
}
}
const p = new MyPromise((resolve, reject) => {
reject('resolve')
})
p.then(res => {
console.log('res', res);
return 'first then'
}, err => {
console.log('err', err);
}).then(res => {
console.log('res', res);
}, err => {
console.log('err', err);
})
// console.log(p);
总结
以上代码整体看起来并不复杂,大家应该都可以看的明白,这里我强调几点:
- Promise.then需要放到微队列中
- Promise.then方法返回的类型不确定,可以是一个函数,也可能不是函数(比如常量),也可能是一个Promise,所以这里的处理复杂一些
- 无论是executor还是onFulfilled, onRejected,都有可能在执行中报错,所以我们需要使用try catch捕获
- 为什么要在then方法和#changeState方法里都调用#run方法,是为了防止你在函数里面写了一个异步,比如以下代码:
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 0)
})
以上就是一个简单的 JavaScript Promise 的手写实现。通过实现这个简易版本的 Promise,我们能更深入地理解 Promise 的原理和机制,也能更好地应用到实际项目中。希望本文对你有所帮助,谢谢阅读!