类与 .then
Promise 标准
具体Promise A+标准可查看 Promise A+ ,这里只拿几个点做简要提及:
- 要求
- "promise" 是一个拥有
then方法的对象或函数,其行为符合本规范; - "thenable" 是一个定义了
then方法的对象或函数,文中译作"拥有then方法" ; - "value" 指任何JavaScript的合法值(包括
undefined,thenable和promise); - "exception" 是使用
throw语句抛出的一个值。 - "reason" 表示一个
promise的拒绝原因。
- "promise" 是一个拥有
- 状态
- 处于等待态时,Promise需满足以下条件:可以迁移至完成态或拒绝态
- 处于完成态时,Promise需满足以下条件:不能迁移至其他任何状态;必须拥有一个不可变的终值
- 处于拒绝态时,Promise需满足以下条件:不能迁移至其他任何状态;必须拥有一个不可变的拒绝原因
类的搭建
基础实现
在使用时都是通过 new Promise 来创建一个 Promise,因此需要创建一个类 MyPromise 。
允许一个函数作为参数,注意,原生的 Promise 回调函数内的代码是同步的,因此这里直接调用。
class MyPromise {
constructor(executor) {
executor()
}
}
const p = new MyPromise((resolve, reject) => {})状态
根据标准,Promise 有三种状态:pending、fulfilled、rejected,并且状态只能从 pending 到 fulfilled 或 rejected,之后不能再改变。
修改状态是通过参数 resolve 和 reject 来实现的,这两个方法都支持接收一个参数。综上所述类新增功能如下:
- 新增状态属性
status,初始值为pending - 新增值
value,初始值为undefined - 新增方法
resolve,接收一个参数,当状态为pending时,将状态改为fulfilled,并将参数赋值给value;如果不是pending状态,则不做任何操作 - 新增方法
reject,接收一个参数,当状态为pending时,将状态改为rejected,并将参数赋值给value;如果不是pending状态,则不做任何操作
class MyPromise {
state = 'pending'
value
constructor(executor) {
const resolve = (val) => {
if (this.state !== 'pending') return
this.value = val
this.state = 'fulfilled'
}
const reject = (reason) => {
if (this.state !== 'pending') return
this.value = reason
this.state = 'rejected'
}
executor()
executor(resolve, reject)
}
}
const p = new MyPromise((resolve, reject) => {
resolve(1)
})优化
初步实现了 Promise 的状态管理,不过需要考虑以下几点:
如果
new MyPromise时传入的executor抛出异常,则MyPromise状态应设置为rejected,并且value为抛出的异常。查看原生Promise也是如此的操作
状态
state应该是内部变量,不应该让外部直接拿到使用修改,因此需要修改为内部私有变量resolve和reject内部相同的代码较多,可以抽离封装成一个公共函数状态如果直接写,后续可能会修改,维护起来很麻烦,最好做成一个常量
根据上方的考虑,修改代码如下:
// 常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
state = 'pending'
#state = PENDING // 修改为内部私有
#value
constructor(executor) {
const resolve = (val) => {
if (this.state !== 'pending') return
this.value = val
this.state = 'fulfilled'
this.#setState(FULFILLED, val)
}
const reject = (reason) => {
if (this.state !== 'pending') return
this.value = reason
this.state = 'rejected'
this.#setState(REJECTED, reason)
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// 修改状态和值
#setState(state, value) {
if (this.#state !== PENDING) return
this.#value = value
this.#state = state
}
}
const p = new MyPromise((resolve, reject) => {
resolve(1)
})
console.log(p.#state) // 报错,不可访问私有属性现在能够解决上述罗列的几个优化点了。不过要注意的是,try...catch 是无法捕获异步的报错,这个原生 Promise 也是如此。

then 方法
基本实现
实现 then 方法,主要功能是接收两个函数参数,当 Promise 状态为 fulfilled 时,执行第一个函数,并传入 value;当 Promise 状态为 rejected 时,执行第二个函数,并传入 reason。
新增一个 then 方法,接收两个参数 onFulfilled 和 onRejected,判断当前的状态 #state,为 fulfilled 时,执行 onFulfilled,并传入 value;为 rejected 时,执行 onRejected,并传入 reason。
// 常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
#state = PENDING // 修改为内部私有
#value
constructor(executor) {
const resolve = (val) => {
this.#setState(FULFILLED, val)
}
const reject = (reason) => {
this.#setState(REJECTED, reason)
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// 修改状态和值
#setState(state, value) {
if (this.#state !== PENDING) return
this.#value = value
this.#state = state
}
then(onFulfilled, onRejected) {
if (this.#state === FULFILLED) {
onFulfilled(this.#value)
}
else if (this.#state === REJECTED) {
onRejected(this.#value)
}
}
}
const p = new MyPromise((resolve, reject) => {
resolve(1)
})
p.then((res) => {
console.log(res) // 1
})链式调用
原生 Promise 是可以通过 .then 链式调用的,参数是上一个 .then 返回的值,而如果直接 return this 返回当前的 MyPromise 类是无法实现功能的。因为当前的 MyPromise 状态已经不是 pending。
所以我们需要在 then 方法中返回一个新的 Promise,并且将 onFulfilled 和 onRejected 的返回值作为参数传递给新的 Promise。
拿到新值后,作为参数调用 resolve 更新 MyPromise 的状态和值。注意,无论是成功还是失败,都是调用的 resolve。
// 常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
#state = PENDING // 修改为内部私有
#value
constructor(executor) {
const resolve = (val) => {
this.#setState(FULFILLED, val)
}
const reject = (reason) => {
this.#setState(REJECTED, reason)
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// 修改状态和值
#setState(state, value) {
if (this.#state !== PENDING) return
this.#value = value
this.#state = state
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
if (this.#state === FULFILLED) {
const res = onFulfilled(this.#value)
resolve(res)
} else if (this.#state === REJECTED) {
const res = onRejected(this.#value)
resolve(res)
}
})
}
}
const p = new MyPromise((resolve, reject) => {
resolve(1)
})
p.then((res) => {
console.log(res) // 1
})考虑异步
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 20)
})基本实现版本,只能处理同步代码,如果 Promise 是异步的,则无法处理,这是因为前面写的判断只判断了 fulfilled 和 rejected 两种状态,并没有处理 pending 状态。而异步代码执行时,Promise 的状态是 pending,因此需要处理 pending 状态。
如果是 pending 状态,则将 onFulfilled 和 onRejected 函数保存起来,等到状态变为 fulfilled 或 rejected 时,再执行。
那么在哪里才能知道 Promise 的状态变为 fulfilled 或 rejected 呢?在 resolve 和 reject 函数中,也就是一开始抽离出来的 #setState 函数。在那里再调用前面保存的 onFulfilled 和 onRejected 函数即可。
// 常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
#state = PENDING // 修改为内部私有
#value
#handler // 保存onFulfilled和onRejected函数
constructor(executor) {
const resolve = (val) => {
this.#setState(FULFILLED, val)
}
const reject = (reason) => {
this.#setState(REJECTED, reason)
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// 修改状态和值
#setState(state, value) {
if (this.#state !== PENDING) return
this.#value = value
this.#state = state
this.#handler()
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this.#handler = () => {
if (this.#state === FULFILLED) {
const res = onFulfilled(this.#value)
resolve(res)
} else if (this.#state === REJECTED) {
const res = onRejected(this.#value)
resolve(res)
}
}
if (this.#state !== PENDING) this.#handler()
})
}
}
const p = new MyPromise((resolve, reject) => {
resolve(1)
})
p.then((res) => {
console.log(res) // 1
})多个实例
接下来看一个场景:
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
p.then((res) => {
console.log('p1', res)
})
p.then((res) => {
console.log('p2', res)
})现在只能打印 p2,p1 没有打印。这是因为最开始运行第一个 then 时 this.#handler 保存了 p1 的回调;然后运行到 p2 后重新赋值,覆盖了 p1 的回调。所以需要把 this.#handler 改成数组,保存多个回调。
// 常量
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
#state = PENDING; // 修改为内部私有
#value;
#handler; // 保存onFulfilled和onRejected函数
#handlers = []; // 保存onFulfilled和onRejected函数
constructor(executor) {
const resolve = (val) => {
this.#setState(FULFILLED, val);
}
const reject = (reason) => {
this.#setState(REJECTED, reason);
}
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
// 修改状态和值
#setState (state, value) {
if (this.#state !== PENDING) return;
this.#value = value;
this.#state = state;
this.#handler();
this.#runTask();
}
#runTask () {
if (this.#state !== PENDING) {
this.#handlers().forEach((cb) => cb());
this.#handlers = [];
}
}
then (onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this.#handler = () => {
this.#handlers.push(() => {
if (this.#state === FULFILLED) {
const res = onFulfilled(this.#value);
resolve(res);
}
else if (this.#state === REJECTED) {
const res = onRejected(this.#value);
resolve(res);
}
}
})
if (this.#state !== PENDING) this.#handler();
this.#runTask();
})
}
}
const p = new MyPromise((resolve, reject) => {
resolve(1);
});
p.then(res => {
console.log('p1', res)
})
p.then(res => {
console.log('p2', res)
})优化
下面有几个小点需要优化一下:
功能优化:需要考虑
return new MyPromise中代码报错的情况,需要和前面一样添加try...catch。代码优化:
if...else里面的代码重复了,可以封装一下。根据 A+ 规范,
.then第一个参数可以不传,下一个.then接收的还是上一个promise的返回值。
// 常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
#state = PENDING // 修改为内部私有
#value
#handlers = [] // 保存onFulfilled和onRejected函数
constructor(executor) {
const resolve = (val) => {
this.#setState(FULFILLED, val)
}
const reject = (reason) => {
this.#setState(REJECTED, reason)
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// 修改状态和值
#setState(state, value) {
if (this.#state !== PENDING) return
this.#value = value
this.#state = state
this.#runTask()
}
#runTask() {
if (this.#state !== PENDING) {
this.#handlers().forEach((cb) => cb())
this.#handlers = []
}
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this.#handlers.push(() => {
if (this.#state === FULFILLED) {
const res = onFulfilled(this.#value)
resolve(res)
}
else if (this.#state === REJECTED) {
const res = onRejected(this.#value)
resolve(res)
}
try {
const cb = this.#state === FULFILLED ? onFulfilled : onRejected
// 如果cb不是函数,说明用户没传,直接返回结果
const res = typeof cb === 'function' ? cb(this.#value) : this.#value
resolve(res)
} catch (err) {
reject(err)
}
})
this.#runTask()
})
}
}
const p = new MyPromise((resolve, reject) => {
resolve(1)
})
p.then((res) => {
console.log('p1', res)
})
p.then((res) => {
console.log('p2', res)
})异步处理
现在的代码写的差不多了,开始查看细节部分。先看一个例子:
const p = new MyPromise((resolve, reject) => {
resolve(1)
})
p.then((res) => {
console.log('then', res)
})
console.log('end')查看上方的打印,.then 里面的代码应该是异步的,因此会先打印 end,再打印 then。但是现在的代码是同步的,因此会先打印 then,再打印 end。
前面所有事件处理的逻辑,都是在 #runTask 函数里面执行的,而目前该方法都是同步的,因此需要将 #runTask 函数改成异步的。
想要实现异步的效果,可以用到 queueMicrotask,但是它有兼容性问题,可以写一个函数,做兼容性处理。
新建一个函数,判断当前环境 queueMicrotask 类型是否是一个函数,如果是说明该方法可用,直接使用;如果不是,则判断当前环境是否是NodeJS,如果是则可以使用 process.nextTick;如果是浏览器环境则没有 process,可以改用 MutationObserver;如果都没有,就只能使用 setTimeout。
// 常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function runMicrotasks(fn) {
if (typeof queueMicrotask === 'function') {
queueMicrotask(fn)
}
else if (
typeof process === 'object' &&
typeof process.nextTick === 'function'
) {
process.nextTick(fn)
}
else if (typeof MutationObserver === 'function') {
const observer = new MutationObserver(fn)
const textNode = document.createTextNode(String(Math.random()))
observer.observe(textNode, { characterData: true })
// 当节点的内容发生变化,就会异步执行前面的fn函数
textNode.data = String(Math.random())
}
else {
setTimeout(fn, 0)
}
}
class MyPromise {
#state = PENDING // 修改为内部私有
#value
#handlers = [] // 保存onFulfilled和onRejected函数
constructor(executor) {
const resolve = (val) => {
this.#setState(FULFILLED, val)
}
const reject = (reason) => {
this.#setState(REJECTED, reason)
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// 修改状态和值
#setState(state, value) {
if (this.#state !== PENDING) return
this.#value = value
this.#state = state
this.#runTask()
}
#runTask() {
runMicrotasks(() => {
if (this.#state !== PENDING) {
this.#handlers().forEach((cb) => cb())
this.#handlers = []
}
})
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this.#handlers.push(() => {
try {
const cb = this.#state === FULFILLED ? onFulfilled : onRejected
const res = typeof cb === 'function' ? cb(this.#value) : this.#value
resolve(res)
} catch (err) {
reject(err)
}
})
this.#runTask()
})
}
}
const p = new MyPromise((resolve, reject) => {
resolve(1)
})
p.then((res) => {
console.log('p1', res)
})
console.log('end').then 返回 Promise
接下来还要考虑一个场景,就是 .then 内 return new Promise 的情况,示例代码如下:
const p = new MyPromise((resolve, reject) => {
resolve(1)
})
const p1 = p.then(() => {
return new Promise((resolve, reject) => {
resolve(2)
})
})
p1.then((res) => {
console.log('p1', res) // p1 { Promise 2 }
})
const pro = new Promise((resolve, reject) => {
resolve(1)
})
const pro1 = pro.then(() => {
return new Promise((resolve, reject) => {
resolve(2)
})
})
pro1.then((res) => {
console.log('pro1', res) // pro1 2
})原生 Promise 返回的是其结果,而我们封装的返回的还是一个 Promise,因此需要修改一下代码,具体要修改的是 then 方法。
原先的代码是直接 resolve(res); 返回结果,现在要先判断一下 res 是否是一个 Promise,如果是返回其 .then 的结果;如果不是,则原样返回。
那么问题来了,如何判断 res 是否是一个 Promise 呢?根据 A+ 规范的说法是,只要这个函数有 .then 方法,那么就默认他是 Promise 函数。
// 常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function runMicrotasks(fn) {
if (typeof queueMicrotask === 'function') {
queueMicrotask(fn)
} else if (
typeof process === 'object' &&
typeof process.nextTick === 'function'
) {
process.nextTick(fn)
} else if (typeof MutationObserver === 'function') {
const observer = new MutationObserver(fn)
const textNode = document.createTextNode(String(Math.random()))
observer.observe(textNode, { characterData: true })
// 当节点的内容发生变化,就会异步执行前面的fn函数
textNode.data = String(Math.random())
} else {
setTimeout(fn, 0)
}
}
function isPromiseLike(obj) {
return typeof obj?.then === 'function'
}
class MyPromise {
#state = PENDING // 修改为内部私有
#value
#handlers = [] // 保存onFulfilled和onRejected函数
constructor(executor) {
const resolve = (val) => {
this.#setState(FULFILLED, val)
}
const reject = (reason) => {
this.#setState(REJECTED, reason)
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// 修改状态和值
#setState(state, value) {
if (this.#state !== PENDING) return
this.#value = value
this.#state = state
this.#runTask()
}
#runTask() {
runMicrotasks(() => {
if (this.#state !== PENDING) {
this.#handlers().forEach((cb) => cb())
this.#handlers = []
}
})
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this.#handlers.push(() => {
try {
const cb = this.#state === FULFILLED ? onFulfilled : onRejected
const res = typeof cb === 'function' ? cb(this.#value) : this.#value
if (isPromiseLike(res)) {
resolve(res.then(resolve, reject))
}
else {
resolve(res)
}
} catch (err) {
reject(err)
}
})
this.#runTask()
})
}
}
const p = new MyPromise((resolve, reject) => {
resolve(1)
})
p.then((res) => {
console.log('p1', res)
})
console.log('end')