类与.then
刀刀
8/8/2025
0 字
0 分钟
Promise 标准
具体 Promise A+ 标准可查看 Promise A+ ,这里只拿几个点做简要提及:
- 要求
- ”promise” 是一个拥有 then 方法的对象或函数,其行为符合本规范;
- “thenable”是一个定义了 then 方法的对象或函数,文中译作“拥有then 方法”;
- “value”指任何 JavaScript 的合法值(包括undefined, thenable 和 promise);
- “exception”是使用 throw语句抛出的一个值。
- “reason”表示一个 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')