大家好,我是东望 ,一个热爱代码的coder。今天我们来聊聊Promise
。相信很多开发者一开始都认为自己对Promise
运用得游刃有余,当遇到一些比较复杂的调用的时候,像进入了死胡同一样,Promise
总是不按套路出牌,有点怀疑自己学的的Promise
是不是假的,用的不是很自信,那是因为我们没有深入研究其内部实现机制,只有我们真正去了解了Promise
原理之后,再用起promise
的时候就会很自信了,游刃有余,如鱼得水,看完我这篇文章,你一定能彻底掌握Promise
手写之前(了解)
在开始手写Promise之前,咱们先简单聊聊几个关键概念,不然一上来就敲代码,很容易写着写着就迷路了。
一、宏任务和微任务
大家应该都遇到过这种情况:
js
console.log('start');
setTimeout(() => {
console.log('timeout');
}, 0);
Promise.resolve().then(() => {
console.log('promise');
});
console.log('end');
按直觉可能觉得 setTimeout
先写的就先执行,但实际输出却是:
js
start
end
promise
timeout
这里为什么会这样呢,这就涉及到宏任务 和微任务的概念了。
- 由于 JavaScript 是单线程 的,如果遇到耗时较长的任务(比如复杂计算或大量循环),页面就会卡顿,无法响应用户操作。为了避免这种情况,JavaScript 引入了「宏任务」和「微任务」两个队列。它们能够将异步任务安排在主线程空闲时执行,从而"假装"实现了并发效果,让页面保持流畅。
- 宏任务(Macrotask): 主要包括
setTimeout
、setInterval
、setImmediate
、事件监听、I/O 操作等。 - 微任务(Microtask): 优先级更高,包括
Promise.then
、queueMicrotask
、process.nextTick
等。
- 宏任务(Macrotask): 主要包括
区别就是,微任务的优先级比宏任务高,哪怕你 setTimeout(0)
,也得等微任务队列清空了才能轮到它。
执行顺序是:
- 主线程执行同步代码
- 遇到宏任务 ➜ 放入宏任务队列
- 遇到微任务 ➜ 放入微任务队列
二、事件循环
JavaScript 虽然是单线程的,但为了处理异步任务,引入了 事件循环(Event Loop) 的机制。
事件循环的执行流程如下:
- 执行同步代码(所有同步任务按顺序执行完)
- 清空微任务队列 (执行
Promise.then
、queueMicrotask
等微任务) - 每执行一个宏任务 (例如
setTimeout
回调),然后回到步骤 2,检查有没有微任务,有的话就执行,循环往复。
所以根据上面的例子执行过程解析👇
js
console.log('start');
setTimeout(() => {
console.log('timeout');
}, 0);
Promise.resolve().then(() => {
console.log('promise');
});
console.log('end');
执行同步代码
console.log('start')
→ 输出start
setTimeout()
→ 宏任务,放入宏任务队列(待定)Promise.resolve().then()
→ 微任务,放入微任务队列(待定)console.log('end')
→ 输出end
清空微任务队列
- 当同步代码执行完发现有微任务,执行
Promise.then()
→ 输出promise
执行一个宏任务
- 当微任务执行完,开始执行
setTimeout()
回调 → 输出timeout
,然会回头看看有没有微任务,每执行一个宏任务之前看看有没有微任务,有的话就执行完所有微任务,再执行宏任务
三、Promise/A+
我们平时用Promise,用得最多的就是 .then()
、.catch()
、.finally()
这些方法了。
但你有没有想过,为什么链式调用方法叫 then
,而不是 next
、andThen
这种更直观的名字?
其实在Promise刚诞生的时候,社区里一堆Promise实现,各有各的写法,甚至连回调执行的时机都不一样,根本没法互相兼容。
为了解决这种混乱局面,社区联合搞了个**Promise/A+**规范,统一了Promise的行为标准,才有了今天大家用得顺畅的Promise。
我们来看看A+规范里,针对常见问题提出的解决方案👇
常见问题 | A+规范条款 | 解决办法 |
---|---|---|
状态乱改 | 2.1.2 状态不可逆 | 定义 pending → fulfilled/rejected 三种状态,状态一旦变化就不能再改了 |
回调执行时机不统一 | 2.2.4 异步执行 | 强制 then 回调必须异步执行,用微任务队列确保时机一致(queueMicrotask / setTimeout(0) ) |
多次调用同一个 then |
2.2.6 多次回调 | then 可以被同一个 Promise 调用多次,每次都得按顺序执行所有回调 |
链式调用中断 | 2.2.7 返回新 Promise | 每次 then 都得返回一个新的 Promise,保证链条不中断 |
空回调默认传递 | 2.2.7.3 空回调处理 | then(null, null) 这种情况也必须正常传递值/错误 |
异常穿透 | 2.2.7.2 错误冒泡 | 让错误跳过非函数回调,直接冒泡到最近的 .catch() |
防止自己套自己 | 2.3.1 防止死循环 | 检查 then 返回的 Promise 是不是自己本身,是就抛错防止死循环 |
返回嵌套 Promise | 2.3.2 透传嵌套 | 如果返回一个 Promise,得用它的最终值来 resolve (解嵌套) |
支持"类Promise"对象 | 2.3.3 处理 thenable | 如果返回的是带 then 方法的对象,也要执行它,并传入 resolve/reject |
多次 resolve/reject | 2.3.3.3.3 | resolve/reject 只允许调用一次,多次调用直接忽略 |
这些规范看着有点多,但理清楚了就会发现:它们都是为了解决我们开发中可能遇到的坑。
接下来,我们就照着这些规则,一步一步手写自己的Promise ------ 亲手打造一个符合Promise/A+规范的Promise 🚀✨。
准备好了么?我们开写!💪
开始手写
先上一个案例给大家参照方便分析
js
const promise = new Promise((resolve, reject) => {
resolve('success')
})
promise.then(value => {
console.log('resolve', value)
})
// 输出 resolve success
根据上面的案例开始手写promise
核心逻辑
一、MyPromise 类核心逻辑实现
1.创建 MyPromise 类,并传入执行器 executor,同时提供 resolve 和 reject 方法
js
// 新建 MyPromise 类
class MyPromise {
constructor(executor) {
const resolve = () => {};
const reject = () => {};
executor(resolve, reject); //立即调用,并传入resolve和reject方法
}
}
2. 确保状态只能从 pending
转变为 fulfilled
或 rejected
,且状态变更后不可逆
我们使用 resolve 和 reject 函数来更改状态,并接收异步信息,使用变量 value 和 reason 来存储成功或失败的结果,其默认值均为 undefined。
js
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(executor) {
//初始化状态pending
this.status = PENDING;
//用于接收成功的值
this.value = undefined;
//用于接收失败的值
this.reason = undefined;
const resolve = (value) => {
// 只有处于pending才能执行,状态已发生变化,不能二次变化
if (this.status === PENDING) {
// 改变状态:fulfilled
this.status = FULFILLED;
// 保存resolve传进来的值
this.value = value;
}
};
const reject = (reason) => {
// 只有处于pending才能执行,状态已发生变化,不能二次变化
if (this.status === PENDING) {
// 改变状态:rejected
this.status = REJECTED;
// 保存reject传进来的失败的值
this.reason = reason;
}
};
executor(resolve, reject);
}
}
3.实现then的方法
js
//my_promise.js
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
};
executor(resolve, reject);
}
// ==== 新增 ====
then(onFulfilled, onRejected) {
// 判断状态
if (this.status === FULFILLED) {
// 调用成功回调,并且把值返回
onFulfilled(this.value);
} else if (this.status === REJECTED) {
// 调用失败回调,并且把原因返回
onRejected(this.reason);
}
}
}
module.exports = MyPromise;
为了后面便于测试和验证,我们将代码通过 module.exports
导出文件名字为my_promise.js
,并在创建一个新的文件 test.js
中进行导入。接下来,我们再使用上面那个出现错误的例子来进行测试。
js
//test.js
const MyPromise = require("./my_promise");
const promise = new MyPromise((resolve, reject) => {
resolve("success");
});
promise.then((value) => {
console.log("resolve", value);
});
//MyPromise:打印结果:success
//Promise: 打印结果:success
执行结果符合正确结果👏👏👏
二、MyPromise 类加入异步处理逻辑
1.基本异步处理
在大多数情况下,resolve
函数是在异步操作(比如网络请求)完成后调用的。但由于这里没有真实的网络请求,我们暂时用 setTimeout
(宏任务)模拟网络请求。
然后我们改造一下案例来看看
js
const MyPromise = require("./my_promise");
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
});
});
promise.then(
(value) => {
console.log("resolve", value);
}
);
// MyPromise执行结果:空
// Promise执行结果:resolve success
此时,我们得到的结果为空,这与预期不符,为什么呢?让我们来深入分析一下。
• 问题分析 :主线程的代码是立即执行的,而 setTimeout
是一个宏任务,它会被加入到宏任务列表中等待执行。因此,then
方法会在 setTimeout
之前被调用。在调用 then
方法时,Promise 的状态仍然是 Pending。由于 then
方法在 Promise 状态为 Pending 时不会执行其回调函数,所以此时并没有任何操作被执行,导致打印的结果为空。这说明 resolve
函数的调用发生在 then
方法的回调之后,从而导致了问题。按照正常的逻辑,resolve
应该在 then
方法之前调用,以改变 Promise 的状态,并随后执行 then
方法的回调。
• 解决思路 :所以为了确保 then
方法的回调总是在 resolve
函数被调用时执行,我们需要对 then
方法的行为进行修改,那该怎么做呢,我们可不可以先暂时存储then方法传入的回调函数,然后然后在 resolve
函数被调用之后再执行这些回调函数,目前看这个思路是可以的,那我们继续上代码。
1.1 引入变量 onFulfilledCallback
和 onRejectedCallback
js
constructor(executor) {
// ==== 新增 ====
// 存储成功回调函数
this.onFulfilledCallback = null;
// 存储失败回调函数
this.onRejectedCallback = null;
//...省略代码
}
1.2 修改 then
方法,支持 PENDING
状态回调存储
js
//优化then方法
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value);
} else if (this.status === REJECTED) {
onRejected(this.reason);
} else if (this.status === PENDING) {
// ==== 新增 ====
// 将成功回调和失败回调存储起来
// 等到reslove函数执行完再执行
// 上面的if的判断条件FULFILLED和REJECTED要保留哦,this.status === PENDING是对异步时候判断操作
this.onFulfilledCallback = onFulfilled;
this.onRejectedCallback = onRejected;
}
}
1.3. resolve 与 reject 中调用回调函数
js
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// ==== 新增 ====
// 判断成功回调是否存在,如果存在就调用
this.onFulfilledCallback?.(value);
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// ==== 新增 ====
// 判断失败回调是否存在,如果存在就调用
this.onRejectedCallback?.(reason);
}
};
修改后的完整代码就是
js
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(executor) {
// ==== 新增 ====
// 存储成功回调函数
this.onFulfilledCallback = null;
// 存储失败回调函数
this.onRejectedCallback = null;
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// ==== 新增 ====
// 判断成功回调是否存在,如果存在就调用
this.onFulfilledCallback?.(value);
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// ==== 新增 ====
// 判断失败回调是否存在,如果存在就调用
this.onRejectedCallback?.(reason);
}
};
executor(resolve, reject);
}
//优化then方法
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value);
} else if (this.status === REJECTED) {
onRejected(this.reason);
} else if (this.status === PENDING) {
// ==== 新增 ====
// 将成功回调和失败回调存储起来
// 等到reslove的时候再执行
// 上面的if的判断条件FULFILLED和REJECTED要保留哦,this.status === PENDING是对异步时候判断操作
this.onFulfilledCallback = onFulfilled;
this.onRejectedCallback = onRejected;
}
}
}
module.exports = MyPromise;
然后我们再执行上面的那个案例试试
js
// 引入我们的 my_promise.js
const MyPromise = require("./my_promise");
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
});
});
promise.then(
(value) => {
console.log("resolve", value);
}
);
// MyPromise执行结果:resolve success
// Promise执行结果:resolve success
这个时候执行结果就不是空了,符合结果,perfect
2.支持 then
的异步规范
根据 Promise/A+ 规范,then
的回调应该是异步执行的(微任务),举个例子
js
// test.js
const MyPromise = require("./my_promise");
const p = new MyPromise((resolve) => resolve());
p.then(() => console.log(1));
console.log(2);
// MyPromise执行结果:1 → 2 (违反规范)
// Promise执行结果:2 → 1
这里的时候为什么会出现这样呢,是因为我们自己的promise
实现,then内部执行的任务是同步任务,所以他先打印1,然后2 ,但是根据根据Promise/A+规范,then
回调必须异步执行,是一个微任务,所以我们要把执行函数内部改成微任务模块执行,那我们怎么改呢,我们这里使用queueMicrotask(mdn介绍如何使用queueMicrotask)把回调变成微任务,继续改代码
js
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(executor) {
this.onFulfilledCallback = null;
this.onRejectedCallback = null;
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// ==== 新增 ====
queueMicrotask(() => this.onFulfilledCallback?.(value));
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// ==== 新增 ====
queueMicrotask(() => this.onRejectedCallback?.(reason));
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
// ==== 新增 ====
queueMicrotask(() => {
onFulfilled(this.value);
});
} else if (this.status === REJECTED) {
// ==== 新增 ====
queueMicrotask(() => {
onRejected(this.reason);
});
} else if (this.status === PENDING) {
this.onFulfilledCallback = onFulfilled;
this.onRejectedCallback = onRejected;
}
}
}
module.exports = MyPromise;
这个时候再执行一下
js
// test.js
const MyPromise = require("./my_promise");
const p = new MyPromise((resolve) => resolve());
p.then(() => console.log(1));
console.log(2);
// MyPromise执行结果:2 → 1 (符合规范)
// Promise执行结果:2 → 1
三、优化 then
方法支持多种 Promise 调用方式
接下来,我们先对 then
方法进行优化。
1.实现 then
方法的多次函数调用并依次执行
老规矩先上案例
js
// test.js
const MyPromise = require("./my_promise");
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 2000);
});
promise.then(() => {
console.log(1);
});
promise.then(() => {
console.log(2);
});
//MyPromise执行结果:2
//Promise执行结果:1→2
目前的代码输出2
,我们怎么能把1
给弄丢了呢!🤔 我们应该留住1
,确保所有 then
中的回调函数都能得到执行。
经过分析,问题出在当有多个 then
方法调用时,后面的 then
的回调会把前一个 then
的赋值给替换掉,所以最后只执行了最后一个 then
的回调。
那么,我们该怎么解决这个问题呢?我们可以将原来的赋值操作替换为数组存储所有的 then
方法,然后依次执行每一个存储的回调,就解决了,继续上代码
下面是修改后的完整代码:
js
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(executor) {
// ==== 修改成数组 ====
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
queueMicrotask(() =>
// ==== 修改 ====
this.onFulfilledCallbacks.forEach((fn) => fn())
);
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
queueMicrotask(() =>
// ==== 修改 ====
this.onRejectedCallbacks.forEach((fn) => fn(reason))
);
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
queueMicrotask(() => {
onFulfilled(this.value);
});
} else if (this.status === REJECTED) {
queueMicrotask(() => {
onRejected(this.reason);
});
} else if (this.status === PENDING) {
// ==== 修改 ====
this.onFulfilledCallbacks.push(() => onFulfilled(this.value));
this.onRejectedCallbacks.push(() => onRejected(this.reason));
}
}
}
module.exports = MyPromise;
接下来,让我们再次尝试运行上面的例子,看看优化后的效果如何
js
// test.js
const MyPromise = require("./my_promise");
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 2000);
});
promise.then(() => {
console.log(1);
});
promise.then(() => {
console.log(2);
});
//MyPromise执行结果:1→2
//Promise执行结果:1→2
结果符合预期,非常棒!接下来,让我们继续下一个 then
的链式调用例子。
2.实现 then
方法的链式调用
2.1 当then函数返回一个普通值
javascript
//test.js
const MyPromise = require("./my_promise");
const promise = new MyPromise((resolve, reject) => {
resolve("success");
});
promise
.then((value) => {
console.log(1);
console.log("resolve", value);
return 2;
})
.then((value) => {
console.log(3);
console.log("resolve", value);
});
//输出结果: .then((value) => { TypeError: Cannot read properties of undefined (reading 'then')
这里报错了TypeError: Cannot read properties of undefined (reading 'then')
为什么会报错呢?原因是当前的 then
方法没有返回任何对象,当你直接在其后调用 .then
时,实际上是在 undefined
上调用 then
,这自然会导致错误。因此,then
方法内部应该返回一个包含 then
方法的对象。
那么,我们是否需要重新编写一个类或者方法呢?答案是否定的。由于 then
方法本身就应该返回一个 Promise 对象,而这个 Promise 对象就包含了 then
方法,所以我们可以直接复用当前的 Promise 类。也就是说,我们可以在 then
方法的实现中,直接返回一个新的 Promise 实例,这样就可以确保每次调用 then
后都能继续链式调用 then
方法了,继续改代码。
js
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(executor) {
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
queueMicrotask(() => this.onFulfilledCallbacks.forEach((fn) => fn()));
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
queueMicrotask(() => this.onRejectedCallbacks.forEach((fn) => fn()));
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const promise2 = new MyPromise((resolve, reject) => {
// 这里的内容在执行器中,会立即执行
if (this.status === FULFILLED) {
// 获取成功回调函数的执行结果作为下一个then函数传进去的值
queueMicrotask(() => {
//then里面return返回的值是作为下一个then的value值
const x = onFulfilled(this.value);
resolve(x);
});
} else if (this.status === REJECTED) {
queueMicrotask(() => {
//同理可得
const x = onRejected(this.reason);
// 成功返回 -> 继续走正常逻辑
resolve(x);
});
} else if (this.status === PENDING) {
//同理可得
this.onFulfilledCallbacks.push(() => {
const x = onFulfilled(this.value);
resolve(x);
});
this.onRejectedCallbacks.push(() => {
const x = onRejected(this.reason);
resolve(x);
});
}
});
return promise2;
}
}
module.exports = MyPromise;
这个时候then就可以返回一个promise对象了,然后我们再使用上面那个例子输出一下
js
// test.js
const MyPromise = require("./my_promise");
const promise = new MyPromise((resolve, reject) => {
// 目前这里只处理同步的问题
resolve("success");
});
promise
.then((value) => {
console.log(1);
console.log("resolve", value);
return 2;
})
.then((value) => {
console.log(3);
console.log("resolve", value);
});
//MyPromise执行结果:
//1
//resolve success
//3
//resolve 2
这个时候结果符合预期,可以正常执行了,鼓掌
2.2 当then函数返回一个Promise
但实际上,我们的 then
方法中的 return
语句不仅可以返回一个普通的值,还可以返回一个 Promise 对象。那么,当我们在 then
方法内部返回一个 Promise 时,又会出现什么结果呢?
js
// test.js
const MyPromise = require("./my_promise");
const promise = new MyPromise((resolve, reject) => {
// 目前这里只处理同步的问题
resolve("success");
});
promise
.then((value) => {
console.log(1);
console.log("resolve", value);
return new MyPromise((resolve, reject) => {
resolve(2);
});
})
.then((value) => {
console.log(3);
console.log("resolve", value);
});
// MyPromoise执行结果
// 1
// resolve success
// 3
// resolve MyPromise {
// onFulfilledCallback: [],
// onRejectedCallback: [],
// status: 'fulfilled',
// value: 2,
// reason: undefined
// }
//promise的执行结果
//1
//resolve success
//3
//resolve 2
这个时候的我们手写返回的promise就出现错误这是为什么呢?
原因是我们只处理了返回值是普通值的情况,而没有处理返回值是 Promise 的情况。当 then
方法返回一个 Promise 时,它会直接将这个 Promise 传递下去,这就是为什么我们看到的状态是 fulfilled
,但实际上我们并没有等到这个返回的 Promise 内部的 resolve
执行成功。
正常的逻辑应该是,当 then
方法返回一个 Promise 时,我们应该等待这个 Promise 解决(即其内部的 resolve
执行成功),然后将解决的值作为下一个 then
方法的输入值。那我们解题思路不就是来了吗,那我们来优化一下如果返回proimise情况。
js
//...省略上面代码
then(onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
queueMicrotask(() => {
const x = onFulfilled(this.value);
//判断返回值是否为promise实例
if (x instanceof MyPromise) {
x.then((promise_value) => {
resolve(promise_value);
});
} else {
resolve(x);
}
});
} else if (this.status === REJECTED) {
queueMicrotask(() => {
const x = onRejected(this.reason);
//同理可得
if (x instanceof MyPromise) {
x.then((promise_value) => {
resolve(promise_value);
});
} else {
resolve(x);
}
});
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(() => {
const x = onFulfilled(this.value);
//判断返回值是否为promise实例
if (x instanceof MyPromise) {
x.then((promise_value) => {
resolve(promise_value);
});
} else {
resolve(x);
}
});
this.onRejectedCallbacks.push(() => {
const x = onRejected(this.reason);
//同理可得
if (x instanceof MyPromise) {
x.then((promise_value) => {
resolve(promise_value);
});
} else {
resolve(x);
}
});
}
});
return promise2;
}
这个时候再调用上面的案例
js
// test.js
const MyPromise = require("./my_promise");
const promise = new MyPromise((resolve, reject) => {
// 目前这里只处理同步的问题
resolve("success");
});
promise
.then((value) => {
console.log(1);
console.log("resolve", value);
return new MyPromise((resolve, reject) => {
resolve(2);
});
})
.then((value) => {
console.log(3);
console.log("resolve", value);
});
//MyPromoise执行结果:
//1
//resolve success
//3
//resolve 2
这个时候这个结果就是正确的了,做到这一步了时候大家是不是都觉Promise基本好像是不是都完成了,手动狗头 ,其实没错这个时候我们基本的功能大部分,然而我们的错误捕捉和处理机制是不是还没开始呢,代码尚未成功,继续卷
四、错误捕捉
1.执行器(executor)中的代码出现错误,导致第一个 then
中的 Promise 被拒绝(reject)
js
constructor(executor) {
//...省略部分代码
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
验证是否可行
js
// test.js
const MyPromise = require("./my_promise");
const promise = new MyPromise((resolve, reject) => {
throw new Error("执行器错误");
});
promise.then(
(value) => {
console.log(value);
},
(error) => {
console.log(error);
}
);
//输出
// Error: 执行器错误
// at E:\源码分析\promise\test.js:4:9
// at new MyPromise (E:\源码分析\promise\my_promise.js:40:7)
// at Object.<anonymous> (E:\源码分析\promise\test.js:3:17)
// at Module._compile (node:internal/modules/cjs/loader:1254:14)
// at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
// at Module.load (node:internal/modules/cjs/loader:1117:32)
// at Module._load (node:internal/modules/cjs/loader:958:12)
// at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
// at node:internal/main/run_main_module:23:47
//正常捕获结果可行
2.实现 then
方法中回调出错时的错误捕获,并将错误传递到下一个 then
js
//编写then方法
then(onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
queueMicrotask(() => {
// ==== 修改 ====
try {
const x = onFulfilled(this.value);
if (x instanceof MyPromise) {
x.then((promise_value) => {
resolve(promise_value);
});
} else {
resolve(x);
}
} catch (error) {
reject(error);
}
});
} else if (this.status === REJECTED) {
queueMicrotask(() => {
// ==== 修改 ====
try {
const x = onRejected(this.reason);
if (x instanceof MyPromise) {
x.then((promise_value) => {
resolve(promise_value);
});
} else {
resolve(x);
}
} catch (error) {
reject(error);
}
});
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(() => {
// ==== 修改 ====
try {
const x = onFulfilled(this.value);
if (x instanceof MyPromise) {
x.then((promise_value) => {
resolve(promise_value);
});
} else {
resolve(x);
}
} catch (error) {
resolve(error);
}
});
this.onRejectedCallbacks.push(() => {
try {
const x = onRejected(this.reason);
//同理可得
if (x instanceof MyPromise) {
x.then((promise_value) => {
resolve(promise_value);
});
} else {
resolve(x);
}
} catch (error) {
reject(error);
}
});
}
});
return promise2;
}
其实在这里的时候我们可以看到then有一些代码是有一些重复性的我们可以抽取公共代码实现复用率,我们来抽取一下
简化后的完整代码
js
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(executor) {
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
queueMicrotask(() => this.onFulfilledCallbacks.forEach((fn) => fn()));
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
queueMicrotask(() => this.onRejectedCallbacks.forEach((fn) => fn()));
}
};
executor(resolve, reject);
}
//编写then方法
then(onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
const handleCallback = (callback, value, resolve, reject) => {
queueMicrotask(() => {
try {
const x = callback(value);
resolvePromise(x,resolve,reject);
} catch (error) {
reject(error);
}
});
};
if (this.status === FULFILLED) {
handleCallback(onFulfilled, this.value, resolve, reject);
} else if (this.status === REJECTED) {
handleCallback(onRejected, this.reason, resolve, reject);
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(() =>
handleCallback(onFulfilled, this.value, resolve, reject)
);
this.onRejectedCallbacks.push(() =>
handleCallback(onRejected, this.reason, resolve, reject)
);
}
});
return promise2;
}
}
// ==== 新增 ====
function resolvePromise(x, resolve, reject) {
if(x instanceof MyPromise) {
x.then(resolve, reject)
} else{
resolve(x)
}
}
module.exports = MyPromise;
验证一下
js
// test.js
const MyPromise = require("./my_promise");
const promise = new MyPromise((resolve, reject) => {
resolve("success");
});
// 第一个then方法中的错误要在第二个then方法中捕获到
promise
.then(
(value) => {
console.log("1", value);
throw new Error("then error");
},
(error) => {
console.log("2", error);
}
)
.then(
(value) => {
console.log("3", value);
},
(error) => {
console.log("4", error);
}
);
//输出
// 1 success
// 4 Error: then error
// at E:\源码分析\promise\test.js:13:13
// at E:\源码分析\promise\my_promise.js:53:25
// at new MyPromise (E:\源码分析\promise\my_promise.js:40:7)
// at MyPromise.then (E:\源码分析\promise\my_promise.js:48:22)
// at Object.<anonymous> (E:\源码分析\promise\test.js:10:4)
// at Module._compile (node:internal/modules/cjs/loader:1254:14)
// at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
// at Module.load (node:internal/modules/cjs/loader:1117:32)
// at Module._load (node:internal/modules/cjs/loader:958:12)
// at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
// 符合结果
五、PromiseA+规范边界代码补充
当我们做到这里的时候我们的promise其实基本已经完善了,但是我们满足PromiseA+规范的条件还有一些没完成
1. 防止死循环 (规范2.3.1)
检查 then
返回的Promise是不是自己本身,是就抛错防止死循环,先上个案例解释一下
js
// test.js
const MyPromise = require("./my_promise");
const P = new MyPromise((resolve, reject) => {
resolve(1);
});
const p1 = P.then((value) => {
console.log(value);
return p1;
});
p1.then((value) => {
console.log(value);
});
//MyPromise:打印结果
// 1
// <ref *1> MyPromise {
// onFulfilledCallbacks: [ [Function (anonymous)] ],
// onRejectedCallbacks: [ [Function (anonymous)] ],
// status: 'fulfilled',
// value: [Circular *1],
// reason: undefined
// }
// Promise执行结果:
// 1
// [TypeError: Chaining cycle detected for promise #<Promise>]
看到这里的时候我们手写promise其实是没有报错得但是他得value: [Circular *1],表示 p1
的 value
属性引用了自己,形成了 循环引用 (即 p1.value
又是 p1
本身)。这就是代码中的"环",也就是 Promise 里的死循环,从代码逻辑上看,p1
是 promise.then
返回的一个新 Promise
,而回调里 return p1
相当于 p1
又依赖 p1
,它在等待自己完成,但自己又依赖自己完成,就像一只狗追着自己的尾巴打转 🐶,永远也停不下来。按照 Promises/A+ 规范 ,当检测到 return
的 Promise
是自身时,应该抛出错误,防止死循环。上代码
js
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(executor) {
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
queueMicrotask(() => this.onFulfilledCallbacks.forEach((fn) => fn()));
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
queueMicrotask(() => this.onRejectedCallbacks.forEach((fn) => fn()));
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
const handleCallback = (callback, value, resolve, reject) => {
queueMicrotask(() => {
try {
const x = callback(value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
if (this.status === FULFILLED) {
handleCallback(onFulfilled, this.value, resolve, reject);
} else if (this.status === REJECTED) {
handleCallback(onRejected, this.reason, resolve, reject);
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(() =>
handleCallback(onFulfilled, this.value, resolve, reject)
);
this.onRejectedCallbacks.push(() =>
handleCallback(onRejected, this.reason, resolve, reject)
);
}
});
return promise2;
}
}
function resolvePromise(promise2, x, resolve, reject) {
// ==== 新增2.3.1 防止循环引用 ====
if (x === promise2) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
module.exports = MyPromise;
2. 空回调默认传递 (规范2.2.7.3)
空回调默认传递这是什么意思呢,举个例子
js
// test.js
const MyPromise = require("./my_promise");
const promise = new MyPromise((resolve, reject) => {
resolve("success");
});
promise
.then()
.then()
.then()
.then(
(value) => console.log(value),
(err) => console.log(err)
);
// MyPromise:打印结果
// TypeError: callback is not a function
这里报错的原因,其实是因为我们的 then
方法没有处理 undefined
、null
或 非函数 的情况。
根据 Promise A+ 规范 2.2.7.3 ,如果 then
的参数不是函数,应该提供一个 默认函数,所以我们来修改一下代码
js
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(executor) {
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
queueMicrotask(() => this.onFulfilledCallbacks.forEach((fn) => fn()));
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
queueMicrotask(() => this.onRejectedCallbacks.forEach((fn) => fn()));
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
// === 新增2.2.7.3:onFulfilled, onRejected不是函数的时候给个默认函数 ===
onFulfilled =typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected = typeof onRejected === "function" ? onRejected: (reason) => {throw reason;};
const promise2 = new MyPromise((resolve, reject) => {
const handleCallback = (callback, value, resolve, reject) => {
queueMicrotask(() => {
try {
const x = callback(value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
if (this.status === FULFILLED) {
handleCallback(onFulfilled, this.value, resolve, reject);
} else if (this.status === REJECTED) {
handleCallback(onRejected, this.reason, resolve, reject);
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(() =>
handleCallback(onFulfilled, this.value, resolve, reject)
);
this.onRejectedCallbacks.push(() =>
handleCallback(onRejected, this.reason, resolve, reject)
);
}
});
return promise2;
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
module.exports = MyPromise;
我们再用上面的例子来试试
js
// test.js
const MyPromise = require("./my_promise");
const promise = new MyPromise((resolve, reject) => {
resolve("success");
});
promise
.then()
.then()
.then()
.then(
(value) => console.log(value),
(err) => console.log(err)
);
// MyPromise:打印结果
// success
符合结果
3. 处理 thenable(规范2.2.3)
什么是thenable呢?thenable 是指一个具有 then
方法的对象或函数,它的行为类似于 Promise。当 then
方法的回调返回一个 thenable 对象时,必须执行该对象的 then
方法,并传入 resolve
和 reject
函数。这确保了 Promise 的链式调用能够继续传递结果或错误,并且处理 thenable 时遵循统一的行为规范。如果 thenable
同时调用 resolve
和 reject
,只认第一个调用,后续调用会被忽略。举个例子
js
// test.js
const MyPromise = require("./my_promise");
// 一个自定义的 thenable 对象
const myThenable = {
then: function (resolve, reject) {
setTimeout(() => {
resolve("success myThenable");
reject("fail myThenable");
}, 1000);
},
};
// 使用 thenable 对象
new MyPromise((resolve, reject) => {
resolve("success");
})
.then((value) => {
console.log(value);
return myThenable;
})
.then((value) => console.log(value));
//MyPromise执行结果:
//success
//{ then: [Function: then] }
//Promise执行结果:
//success
//success myThenable
也就是说,当我们处理一个 myThenable
对象时,应该对其内部的 then
方法进行处理,并传入 resolve
和 reject
来处理它的异步结果,而不是直接返回原始值,那我们改改代码
js
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(executor) {
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
queueMicrotask(() => {
this.onFulfilledCallbacks.forEach((fn) => fn());
});
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
queueMicrotask(() => {
this.onRejectedCallbacks.forEach((fn) => fn());
});
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (reason) => {
throw reason;
};
const promise2 = new MyPromise((resolve, reject) => {
const handleCallback = (callback, value, resolve, reject) => {
queueMicrotask(() => {
try {
const x = callback(value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
if (this.status === FULFILLED) {
handleCallback(onFulfilled, this.value, resolve, reject);
} else if (this.status === REJECTED) {
handleCallback(onRejected, this.reason, resolve, reject);
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(() =>
handleCallback(onFulfilled, this.value, resolve, reject)
);
this.onRejectedCallbacks.push(() =>
handleCallback(onRejected, this.reason, resolve, reject)
);
}
});
return promise2;
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
// 如果 x 是一个 thenable或者promise 对象
if (x !== null && (typeof x === "object" || typeof x === "function")) {
// 调用 thenable 的 then 方法,并传递 resolve 和 reject
let then;
try {
// 把 x.then 赋值给 then
then = x.then;
} catch (error) {
// 如果取 x.then 的值时抛出错误 error ,则以 error 为据因拒绝 promise
return reject(error);
}
// 如果 then 是函数,比如thenable 的 then 方法,或者promise
if (typeof then === "function") {
//如果 thenable 同时调用 resolve 和 reject,只认第一个调用,
let called = false;
try {
then.call(
x, // this 指向 x
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
(y) => {
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject); // 递归解析 y
},
// 如果调用 then 方法抛出了异常 error:
// 如果 resolvePromise 或 rejectPromise 已经被调用,直接返回
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} catch (error) {
// 如果调用 then 方法抛出了异常 error:
// 如果 resolvePromise 或 rejectPromise 已经被调用,直接返回
if (called) return;
// 否则以 error 为据因拒绝 promise
reject(error);
}
} else {
// 如果 then 不是函数,以 x 为参数执行 promise,把整个myThenable返回出去
//比如这种情况
// const myThenable = {
// then: success
// };
resolve(x);
}
} else {
resolve(x);
}
}
用上面的示例测试后,结果已验证符合预期(这里测试没问题,大家可以自行尝试,代码就不再重复展示啦)。
PromiseA+检测
在前面的内容中,我们基于 PromiseA+ 规范 实现了一个自定义的 Promise
,但目前只是根据规范进行编写,并没有经过严格的测试。为了确保我们的 Promise
实现真正符合 PromiseA+ 规范 ,我们使用官方提供的测试工具 ------ promises-aplus-tests
进行验证。
1. 安装 promises-aplus-tests
首先,我们需要安装 promises-aplus-tests
这个测试工具:
js
npm install promises-aplus-tests -D
这个工具会自动运行一系列测试用例,检查我们的 Promise
是否遵循 PromiseA+ 规范。
2. 在 Promise
代码中添加 deferred
方法
为了让 promises-aplus-tests
能够正确测试我们的 Promise
,我们需要在 MyPromise
代码中引入promises-aplus-tests
包添并加一个 deferred
方法:
js
const promisesAplusTests = require("promises-aplus-tests");
class MyPromise {
// ...... 省略已有代码
}
// 添加 deferred 方法
MyPromise.deferred = function () {
var result = {};
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve;
result.reject = reject;
});
return result;
};
promisesAplusTests(MyPromise, function (err) {
if (err) {
console.error("Promises/A+ 检测失败");
console.error(err);
} else {
console.log("Promises/A+ 检测通过");
}
});
// 导出 MyPromise 供测试工具使用
module.exports = MyPromise;
为什么需要 deferred
方法?
promises-aplus-tests
需要deferred
方法来创建Promise
实例,并提供resolve
、reject
方法做状态切换。
3. 开启测试
当 MyPromise
代码准备好后,我们就可以使用以下命令运行测试:
js
node my_promise.js
当运行这段脚本是不是很期待呢,看自己的写的promise符不符合规范,嘿嘿
js
√ eventually-rejected
The value is `true` with `Boolean.prototype` modified to have a `then` method
√ already-fulfilled
√ immediately-fulfilled
√ eventually-fulfilled
√ already-rejected
√ immediately-rejected
√ eventually-rejected
The value is `1` with `Number.prototype` modified to have a `then` method
√ already-fulfilled
√ immediately-fulfilled
√ eventually-fulfilled
√ already-rejected
√ immediately-rejected
√ eventually-rejected
872 passing (21s)
Promises/A+ 测试通过
看这个打印的意思手的是我们的
完美通过该测试
Promise 的实例方法和静态方法补充
在上面,我们已经手写了一个符合 Promise/A+ 规范的 Promise
实现。不过,目前还缺少了一些常见的静态方法与实例方法,严格来说 ,Promise/A+ 规范的核心目标是定义 then
方法的行为,以及 Promise
的状态管理逻辑,并不包括这些方法,这些方法本质上都是对 then
逻辑的封装,都是ES6进一步封装的,类似于 语法糖 ,所以,如果你只是想了解 Promise/A+ 的实现原理,到这里就已经足够了 ,但如果你想搞清楚 Promise.resolve()
、Promise.all()
、Promise.race()
、Promise.catch()
、Promise.finally()
等方法是怎么实现的,那就接着往下看 🚀!
一、实例方法
1. catch
捕获 Promise
的异常,相当于 then(undefined, onRejected)
。
js
class MyPromise {
//...省略其他代码
catch(onRejected) {
return this.then(null, onRejected);
}
}
2. finally
无论 Promise
状态如何,都会执行 callback
。
js
class MyPromise {
//...省略其他代码
finally(onFinally) {
this.then(
() => {
onFinally();
},
() => {
onFinally();
}
);
}
}
二、静态方法
1. Promise.resolve
Promise.resolve
返回一个已解决(fulfilled)的 Promise
,其值为 value
。如果 value
本身是一个 Promise
,则直接返回它。
js
class MyPromise {
//...省略其他代码
// resolve 静态方法
static resolve(value) {
// 如果传入的是 MyPromise 实例,则直接返回
if (value instanceof MyPromise) {
return value;
}
// 否则,将其包装成一个新的 Promise
return new MyPromise(resolve => {
resolve(value);
});
}
}
实现逻辑:
- 如果
value
已经是MyPromise
实例,则直接返回,避免重复包装。 - 否则,创建一个新的
MyPromise
,并立即调用resolve
使其进入fulfilled
状态。
2. Promise.reject
Promise.reject
返回一个 已拒绝(rejected) 的 Promise
,其原因(reason)由调用时传入的参数决定。
js
class MyPromise {
//...省略其他代码
// reject 静态方法
static reject(reason) {
return new MyPromise((_, reject) => {
reject(reason);
});
}
}
实现逻辑:
- 直接创建一个新的
Promise
,并立即调用reject
让其进入rejected
状态。 - 无论
reason
是什么,Promise.reject
都不会进行包装,而是直接传递给reject
。
3. Promise.all
Promise.all
需要等待 所有 Promise 完成(fulfilled),才会返回一个包含所有结果的数组。如果其中任何一个 Promise
失败(rejected),整个 Promise.all
都会立即失败。
js
class MyPromise {
//...省略其他代码
// all 静态方法
static all(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument must be an array"));
}
let results = [];
let count = 0;
if (promises.length === 0) {
return resolve(results);
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise)
.then((value) => {
results[index] = value;
count++;
if (count === promises.length) {
resolve(results);
}
})
.catch(reject);
});
});
}
}
实现逻辑:
- 维护一个
results
数组和count
计数器,记录已完成的Promise
数量。 - 逐个解析
Promise
,如果全部成功,返回所有结果;如果有一个失败,立即reject
整个Promise.all
。
4. Promise.race
Promise.race
返回 最先执行完成的 Promise
(无论是 fulfilled
还是 rejected
)。
js
class MyPromise {
//...省略其他代码
// race 静态方法
static race(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument must be an array"));
}
promises.forEach(promise => {
MyPromise.resolve(promise).then(resolve, reject);
});
});
}
}
实现逻辑:
- 遍历
promises
数组,任何一个Promise
最先 变成fulfilled
或rejected
,都会立即触发resolve
或reject
。
5. Promise.allSettled
Promise.allSettled
等待 所有 Promise
完成(无论是 fulfilled
还是 rejected
),并返回每个 Promise
的状态和结果。
js
class MyPromise {
//...省略其他代码
// allSettled 静态方法
static allSettled(promises) {
return new MyPromise((resolve) => {
if (!Array.isArray(promises)) {
return resolve(new TypeError("Argument must be an array"));
}
let results = [];
let count = 0;
if (promises.length === 0) {
return resolve(results);
}
promises.forEach((promise, index) => {
MyPromise.resolve(promise)
.then(
(value) => {
results[index] = { status: FULFILLED, value };
},
(reason) => {
results[index] = { status: REJECTED, reason };
}
)
.finally(() => {
count++;
if (count === promises.length) {
resolve(results);
}
});
});
});
}
}
实现逻辑:
- 维护
results
数组,存储每个Promise
的状态和结果(fulfilled
或rejected
)。 - 所有
Promise
完成后,返回包含所有状态的数组。
6. Promise.any
Promise.any
只要 至少有一个 Promise
成功 ,就返回该 Promise
的值。如果 所有 Promise
都失败 ,则返回 AggregateError
。
js
class MyPromise {
//...省略其他代码
// any 静态方法
static any(promises) {
return new MyPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument must be an array"));
}
let errors = [];
let count = 0;
promises.forEach(promise => {
MyPromise.resolve(promise).then(resolve, err => {
errors.push(err);
count++;
if (count === promises.length) {
reject(new AggregateError(errors, "All promises were rejected"));
}
});
});
});
}
}
实现逻辑:
- 如果有
Promise
成功,立即resolve
。 - 如果所有
Promise
都失败,返回AggregateError
。
Promise核心要点总结
1. 核心机制
- 链式调用 :
.then()
始终返回新Promise,形成独立调用链,避免状态污染。 - 异步优先级 :通过微任务队列 确保回调执行顺序优先于
setTimeout
等宏任务。 - 错误冒泡 :链式调用中未处理的错误会穿透到最近的
.catch()
,实现集中捕获。
2. 实践挑战
建议:手写Promise时重点关注:
- 状态机实现(
pending
/fulfilled
/rejected
) - 回调队列存储(处理多次调用
.then()
) - 返回值解析(支持普通值、Promise、
thenable
对象) - 循环引用检测(避免
promise.then(() => promise)
导致死锁)
3. 巩固练习
手写完 Promise 后,大家可以多做一些关于 Promise 的题目,从源码角度思考问题。这样才能真正掌握 Promise 的底层原理,而不仅仅是 API 调用大师。
彩蛋
想体验被源码支配的恐惧?
自己动手写个迷你Promise吧!
(写崩了别找我,写成了记得点个赞~)
关注我,下期揭秘Async/Await如何把Promise按在地上摩擦!
(Promise:???)