中文: 实现一个最基础的 Promise,只支持:
状态管理,state状态的三种值:
resolve / reject 方法
then(只执行一次,不考虑链式)
English: Implement a minimal Promise that supports:
then (no chaining required)分析:
要结合Promise的定义、作用、用法,还有class的用法来理解。参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
1、定义:Promise是JS中用于处理异步操作的一种对象。可以使用prototype的方式来实现,但语法太琐碎,所以最好使用class来实现,它语法直观、可读性强。
xxxxxxxxxxclass Promise { }2、Promise的构造函数怎么写?还是要参考文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise

这是最难理解的部分,要根据Promise的用法来做。
仔细看executor的定义。①它是一个函数。②它接收两个参数,resolveFunc和rejectFunc。③它返回的是一个promise对象,这个现在不用管,只需要返回一个函数或者reject(err)即可。
疑问:
① 既然 new Promise(executor) 的 executor 函数接收 (resolve, reject) 参数,为什么resolveFunc和rejectFunc需要在constructor里面来直接实现?为什么不是 executor 函数接收 (resolve, reject) 参数传递过来的?
主要原因有两点:
1、它们是 Promise 内部状态的“钥匙”
resolve 和 reject 的唯一任务就是:修改这个 Promise 实例的内部状态(从 pending 变为 fulfilled 或 rejected)。
constructor 里精准地创建两个“专属于当前实例”的函数。这两个函数通过闭包直接绑定了当前实例的私有状态(比如 this.state)。2、控制权的安全移交(Privilege Design)
Promise 设计遵循一种“特权模式”:只有创建 Promise 的人,才有权决定它什么时候成功或失败。
executor 的参数偷偷塞给开发者。executor 执行完毕,除了拿到这两个按钮的人,外界(比如调用 .then() 的人)是完全触碰不到 resolve 和 reject 的。如果你尝试从外部传入这两个函数,代码会变得逻辑混乱。比如:
xxxxxxxxxx// 假设按照你设想的“外部传参”方式(这是错误的、不存在的写法)const myResolve = (val) => { /* 我该怎么修改 p 的内部状态? */ };const p = new Promise(myResolve, myReject, (res, rej) => { });你会发现 myResolve 根本不知道它要“解决”哪一个 Promise 实例。
总结
在 constructor 内部实现这两个函数,本质上是 Promise 在向你发出邀请:
“嘿,我为你这个实例专门定制了两个改状态的工具,我现在通过
executor的参数交给你。只有你能用它们来告诉我任务结束了,别人都改不了我。”
这就是为什么我们在实现 MyPromise 时,必须在 constructor 内部手动写好 const resolve = ...,然后把这个定义好的函数引用传给 executor。
理解了这个设计,你也就理解了 Promise 是如何保证状态安全和不可逆的。
最难理解的就是:
一般我们在new一个实例的时候,会传实参进去,比如说
new Animal("Sheep"),传递的是实参。如果需要传递一个函数进去呢?像这样:new MyPromise(() => { ... }),我们也不难理解。难就难在,
new Promise传递的参数executor,要写成这样:xconst p = new Promise((resolve, reject) => {const data = fetch("...")if(data.success) {resolve("异步数据");} else {reject("异步数据");}});这里的两个形参resolve和reject,让人很难理解。关键是这两个形参也是函数,而且是不需要我们实现的函数(在Promise类中就已经实现了)。
按理说,传入函数的函数参数,我们应该知道是起什么作用的,才比较好理解,但是这里确实很抽象。
② contructor里面很抽象,用到了哪些js技术?
1、闭包 (Closure) —— 最关键的基石
哪里用了: 在 constructor 内部定义的 const resolve 和 const reject。
为什么难懂: 这两个函数在 constructor 执行完之后,依然能“记住”并操作 this.state、this.value 等变量。
通俗解释: 这就像你在家里(constructor)留了两个遥控器给客人(executor)。即便你离开了家,客人按下遥控器,依然能控制你家里的电视机。
2、箭头函数 (Arrow Functions) 与 this 绑定
哪里用了: const resolve = (value) => { ... }。
为什么难懂: 如果你用普通的 function 定义,当它在 executor 里被调用时,this 可能会指向全局对象或 undefined。
通俗解释: 箭头函数能自动捕捉定义时所在的 this。它确保了无论 resolve 被丢到哪里执行,它改的一定是当前这个 Promise 实例的状态。
3、控制权反转 (Inversion of Control)
哪里用了: executor(resolve, reject)。
为什么难懂: 通常是我们调用系统的函数,但这里是 Promise 调用你传入的函数。
通俗解释: 这叫“不要打电话给我们,我们会给你打电话”。Promise 准备好了工具(resolve/reject),然后通过回调把工具递到你手里,让你在自己的逻辑里决定什么时候用。
4、状态机模型 (State Machine)
哪里用了: this.state = "pending" 以及改变状态时的 if 判断。
为什么难懂: 因为它要求逻辑是“原子化”的。
通俗解释: Promise 像是一个有三种颜色的灯。你的逻辑确保了灯只能从“灰色(pending)”变到“绿色(fulfilled)”或“红色(rejected)”,且一旦变色,就再也按不动开关了。
5、异常处理 (Try...Catch)
哪里用了: try { executor(...) } catch (err) { reject(err) }。
为什么难懂: 它捕捉的是外部传入的代码。
通俗解释: 这是一个“安全气囊”。万一用户在 executor 里的代码写错了(比如变量名写错),MyPromise 不会直接让整个程序崩溃,而是优雅地自动切换到“失败状态”。
③ executor的作用是什么?(这个要重点理解)
可以结合实际使用来理解,最经典的就是获取api接口数据之后做下一步的动作。
xxxxxxxxxx// 模拟一个请求后端的函数,它返回一个 Promisefunction loginApi(username, password) { return new Promise((resolve, reject) => { console.log("正在连接服务器..."); setTimeout(() => { if (username === "admin" && password === "123") { resolve({ token: "ABC-123", userId: 1 }); // 登录成功 } else { reject("用户名或密码错误"); // 登录失败 } }, 2000); // 模拟 2 秒的网络延迟 });}// 实际项目中的逻辑应用function handleLogin() { const btn = { loading: true }; // 按钮进入加载状态 console.log("按钮开始转圈..."); loginApi("admin", "123") .then((userData) => { // 这里的 userData 就是上面 resolve 传出来的对象 console.log("登录成功,用户令牌:", userData.token); console.log("正在跳转到主页..."); }) .catch((error) => { // 这里的 error 就是上面 reject 传出来的字符串 console.error("登录失败提示:", error); alert("请检查账号密码!"); }) .finally(() => { // 无论成功还是失败,最后都要把加载状态关掉 btn.loading = false; console.log("按钮停止转圈。"); });}// 执行handleLogin();这个例子可以很好的说明,Promise能够让你不用关心任务什么时候结束(因为用到Promise的地方,可能都会有异步操作),在new的时候告诉它执行什么任务,之后只需要通过 then 告诉它“之前的任务结束之后该干嘛”。
代码如下:
xxxxxxxxxxclass MyPromise { constructor(executor) { // 状态 this.state = "pending"; this.value = undefined; this.reason = undefined; // 成功的处理函数 const resolve = (value) => { // 防止多次调用 resolve / reject if (this.state === "pending") { this.state = "fulfilled"; this.value = value; } }; // 失败的处理函数 const reject = (reason) => { // 防止多次调用 resolve / reject if (this.state === "pending") { this.state = "rejected"; this.reason = reason; } }; try { executor(resolve, reject); } catch (err) { reject(err); } }}下面是对代码的解释:
- Promise 的定义:它是一个“状态机”
在你的代码里,
class MyPromise就是在定义这个状态机。
作用:Promise 是一个用于表示异步操作结果的对象,它本质是一个状态机 + 回调容器。
Promise是状态机:
this.state = "pending":接水中(还没满,也没漏)。👉 有三种状态:pending / fulfilled / rejected 👉 状态只能改变一次
Promise 保存“未来的结果”,供then来消费
this.value:接满后的那一杯水。this.reason:如果饮水机坏了,坏的原因。
- 什么是 Executor(执行器)?
executor是连接“Promise 内部逻辑”和“外部业务逻辑”的桥梁。
作用:它让你在创建 Promise 的那一刻,就立刻开始执行你的业务(比如开始接水)。
代码体现:
xxxxxxxxxx// 你的构造函数里这一行最关键executor(resolve, reject);你把内部写好的两个“开关”函数
resolve和reject传给用户。用户在业务成功时拨动resolve开关,失败时拨动reject开关。
- 用法:如何使用这个最简版?
假设我们要模拟一个同步的业务逻辑(因为你这个版本暂不支持异步):
xxxxxxxxxx// 1. 创建实例(也就是执行了 constructor)const myP = new MyPromise((resolve, reject) => {// 这部分就是 executorconsole.log("1. 业务开始执行...");const success = true;if (success) {resolve("这是结果数据"); // 2. 拨动成功开关} else {reject("出错了");}});// 3. 调用 then(也就是执行了 then 方法)myP.then((data) => { console.log("3. 成功回调执行:", data); },(err) => { console.log("失败回调执行:", err); });
结合你的代码看流程
初始化:当你
new MyPromise时,构造函数运行,状态设为pending。执行任务:构造函数里的
executor立即执行。状态变更:
- 在
executor里你调用了resolve("数据")。- 这会触发你代码里定义的
const resolve = (value) => { ... }。- 关键点:此时
this.state从pending变成了fulfilled,this.value存下了"数据"。处理结果:
- 当你调用
.then()时,then内部会判断if (this.state === "fulfilled")。- 因为状态已经变了,它会立刻执行你传给
then的第一个参数(函数),并把this.value传进去。总结
- Promise 是什么? 是一个管理任务状态的盒子。
- 作用是什么? 让你不用关心任务什么时候结束,只需要通过
then告诉它“结束之后该干嘛”。- 用法是什么?
new的时候写任务逻辑,.then的时候写结果处理逻辑。
3、.then()方法最多接收两个参数。第一个参数是 Promise 兑现时的回调函数,第二个参数是 Promise 拒绝时的回调函数。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/then


但是在这个例子中,我们返回的不是一个Promise对象,而是直接执行then,所以里面只需要根据state状态,来判断是执行onFulfilled还是onRejected函数,并传递相应的参数即可。
xxxxxxxxxxclass MyPromise { constructor(executor) { } then(onFulfilled, onRejected) { if(this.state === "fulfilled") { onFulfilled(this.value) } if(this.state === "rejected") { onRejected(this.reason) } }}测试:
针对你实现的那个最简版 MyPromise,我们可以通过以下测试案例来验证它的核心逻辑(同步执行、状态锁定、异常捕获)。
你可以将这些代码直接粘贴到你的 MyPromise 类下方运行:
这是最基础的场景,验证 resolve 是否能正常改变状态并触发 then。
x
console.log("--- 测试 1:同步 Resolve ---");const p1 = new MyPromise((resolve, reject) => { resolve("成功数据");});p1.then( data => console.log("测试 1 成功回调:", data), // 预期输出: 成功数据 err => console.log("测试 1 失败回调:", err));
Promise 的规范要求状态一旦改变就不能再变。这个案例验证你的 if (this.state === "pending") 判断是否生效。
x
console.log("\n--- 测试 2:状态锁定 ---");const p2 = new MyPromise((resolve, reject) => { resolve("第一次成功"); reject("尝试失败"); // 这一行不应该生效 resolve("第二次成功"); // 这一行也不应该生效});p2.then( data => console.log("测试 2 结果:", data), // 预期输出: 第一次成功 err => console.log("测试 2 错误:", err));
验证当 executor 内部发生代码错误时,Promise 是否能自动转为 rejected 状态。
x
console.log("\n--- 测试 3:捕获异常 ---");const p3 = new MyPromise((resolve, reject) => { throw new Error("代码报错了!"); // 模拟代码运行崩溃});p3.then( data => console.log("测试 3 成功:", data), err => console.log("测试 3 捕获到错误:", err.message) // 预期输出: 代码报错了!);
为了让你看清这个版本的局限性,你可以运行下面这个案例。你会发现控制台什么都不会输出:
x
console.log("\n--- 测试 4:异步(会失败) ---");const p4 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve("异步数据"); }, 1000);});p4.then(data => console.log("测试 4 异步结果:", data)); // 结果:控制台无输出。// 原因:then 执行时状态还是 pending,但是then方法代码里面没有this.state === "pending"时的处理逻辑,所以什么都不会执行。等到1s之后,会执行resolve("异步数据"),但只是执行resolve函数而已,resolve函数里面是怎么写的?只是两个赋值操作,所以不会有任何输出。总结
你的最简版代码已经成功实现了:
从上面的测试4可以看出,如果任务是异步的,then执行的时候状态还是pending,现有的then函数里面没有对pending进行处理。该怎么处理呢?
Question(核心)就是:如果 state 是 pending,then 应该做什么?
中文: 如果 state 是 pending,then 应该把回调保存起来,等待之后执行
English:
If the state is pending, then should store the callbacks and execute them later.
中文: Promise 本质是一个“发布-订阅模型”,then 是订阅,resolve/reject 是发布。
English:
Promise is essentially a publish-subscribe model: then subscribes, and resolve/reject publish.
新增两个队列:
xxxxxxxxxxthis.onFulfilledCallbacks = [];this.onRejectedCallbacks = [];在then中,处理state为“pending”时的逻辑,也就是store the callbacks。
xxxxxxxxxxthen(onFulfilled, onRejected) { if (this.state === "fulfilled") { onFulfilled(this.value); } if (this.state === "rejected") { onRejected(this.reason); } if(this.state === 'pending') { this.onFulfilledCallbacks.push(onFulfilled) this.onRejectedCallbacks.push(onRejected) }}修改resolve和reject,当异步任务完成后,执行resolve或者reject的时候,将队列里面的callbacks取出来执行。
xxxxxxxxxx// 成功的处理函数const resolve = (value) => { // 防止多次调用 resolve / reject if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach(fn => fn(this.value)) } };// 失败的处理函数const reject = (reason) => { // 防止多次调用 resolve / reject if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach(fn => fn(this.reason)) } };完整代码:
xxxxxxxxxxclass MyPromise { constructor(executor) { // 状态 this.state = "pending"; this.value = undefined; this.reason = undefined; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; // 成功的处理函数 const resolve = (value) => { // 防止多次调用 resolve / reject if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach(fn => fn(this.value)) } }; // 失败的处理函数 const reject = (reason) => { // 防止多次调用 resolve / reject if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach(fn => fn(this.reason)) } }; try { executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled, onRejected) { if (this.state === "fulfilled") { onFulfilled(this.value); } if (this.state === "rejected") { onRejected(this.reason); } if(this.state === 'pending') { this.onFulfilledCallbacks.push(onFulfilled) this.onRejectedCallbacks.push(onRejected) } }}测试:
xxxxxxxxxxconsole.log("\n--- 测试 4:异步 ---");const p4 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve("异步数据"); }, 1000);});p4.then(data => console.log("测试 4 异步结果:", data)); 
注意执行流程:
executor里面先执行setTimeout,state没有变化;然后执行then,走then里面的state === 'pending'这段代码,然后将data => console.log(data)这段代码放到onFulfilledCallbacks中;等到1s之后,执行resolve()函数,从onFulfilledCallbacks中取出全部函数来执行。
要求:
返回新的 Promise
处理:
then 必须返回一个新的 Promise,是为了实现:
每次 then 都返回一个新的 Promise,才能继续调用 then
上一个 then 的返回值,会作为下一个 then 的输入
每个 then 都是一个独立的 Promise,互不影响
错误可以沿着链一直传递到 catch
then 返回新的 Promise,是为了把“当前回调的执行结果”封装成一个新的异步任务,从而实现链式调用和状态传递。
Standard Answer (English)
then must return a new Promise to enable:
then returns a new Promise to wrap the result of the current callback into a new asynchronous task, enabling chaining and value propagation.
1、then的返回值必须是一个promise
xxxxxxxxxxthen(onFulfilled, onRejected) { return new MyPromise((resolve, reject) => { });}2、需要处理onFulfilled、onRejected函数执行后的返回值。
接收返回值:
xxxxxxxxxxconst result = onFulfilled(this.value);// 或者const result = onRejected(this.reason);返回值可能有三种类型:
普通值
xxxxxxxxxx.then(() => 1)返回值是1,直接当作成功结果处理即可。
xxxxxxxxxxresolve(1)
返回promise对象(这个是最难理解的)
xxxxxxxxxx.then(() => Promise.resolve(2))返回值是一个promise对象,接管它的状态。如果promise里面的任务状态变为fulfilled,就交给resolve处理;否则交给reject处理。
resolve和reject本身就是函数。
xxxxxxxxxxresult.then(resolve, reject)
错误
xxxxxxxxxx.then(() => { throw new Error("err")})使用try...catch来处理,在catch中reject(err)抛出错误。
xxxxxxxxxxtry { const result = } catch (e) { reject(e)}3、使用instanceof来判断某个对象是否属于Promise的实例
4、如果new Promise()里面的任务是异步任务,当then执行之后,不能简单的将onFulfilled、onRejected方法推送到队列中去,应该对它们进行同样的处理,然后这个then后面的.then().then()能够得到链式调用。
5、疑问:
为什么this.state === 'rejected'了,不直接onRejected(this.reason)执行呢?还要像fulfilled那样继续进行处理呢?
我被rejected的拒绝,这个翻译误导了。这个rejected并不等于是错误,而是一种状态。比如说一个任务,如果做成了接下来就执行一种方案,如果没有做成接下来就执行另外的方案,都是可以继续下去的,而且后面还会有两种方案等着,所以肯定是要继续处理的。 这和错误是完全不同的,要理清楚。
为了避免错误时程序停止执行,那么就需要使用try...catch来捕获错误,供接下来处理。
代码如下:
x
class MyPromise { constructor(executor) { this.state = "pending"; this.value = undefined; this.reason = undefined; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach((fn) => fn(value)); } }; const reject = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => fn(reason)); } }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { return new MyPromise((resolve, reject) => { if (this.state === "fulfilled") { try { const result = onFulfilled(this.value); if (result instanceof MyPromise) { result.then(resolve, reject); } else { resolve(result); } } catch (e) { reject(e); } } if (this.state === "rejected") { try { const result = onRejected(this.reason); if (result instanceof MyPromise) { result.then(resolve, reject); } else { resolve(result); } } catch (e) { reject(e); } } if (this.state === "pending") { this.onFulfilledCallbacks.push(() => { try { const result = onFulfilled(this.value); if (result instanceof MyPromise) { result.then(resolve, reject); } else { resolve(result); } } catch (e) { reject(e); } }); this.onRejectedCallbacks.push(() => { try { const result = onRejected(this.reason); if (result instanceof MyPromise) { result.then(resolve, reject); } else { resolve(result); } } catch (e) { reject(e); } }); } }); }}上面的代码实现有重复的部分,可以将try...catch部分封装为一个函数,然后调用即可:
xxxxxxxxxxclass MyPromise { constructor(executor) { this.state = "pending"; this.value = undefined; this.reason = undefined; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach((r) => r(this.value)); } }; const reject = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach((r) => r(this.reason)); } }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { return new Promise((resolve, reject) => { const handle = (callback, value) => { try { const result = callback(value); if (value instanceof MyPromise) { result.then(resolve, reject); } else { resolve(result); } } catch (err) { reject(err); } }; if (this.state === "fulfilled") { handle(onFulfilled, this.value); } if (this.state === "rejected") { handle(onRejected, this.reason); } if (this.state === "pending") { this.onFulfilledCallbacks.push(() => handle(onFulfilled, this.value)); this.onRejectedCallbacks.push(() => handle(onRejected, this.reason)); } }); }}测试:
验证 .then() 返回普通值时,能否正确传给下一个 .then()。
x
console.log("--- 测试 1:同步链式调用 ---");const p1 = new MyPromise((resolve) => { resolve(10);});p1.then(res => { console.log("第一个 then:", res); // 10 return res * 2;}).then(res => { console.log("第二个 then:", res); // 20});
这是 Promise 最强大的地方:如果第一个 then 返回一个 新的 Promise,第二个 then 是否会等待它完成?
x
console.log("\n--- 测试 2:异步链式调用 ---");const p2 = new MyPromise((resolve) => { setTimeout(() => resolve("第一阶段完成"), 1000);});p2.then(res => { console.log("P2 第一步:", res); // 返回一个新的 Promise return new MyPromise((resolve) => { setTimeout(() => resolve("第二阶段完成"), 1000); });}).then(res => { console.log("P2 第二步:", res); // 应该在第一步 1 秒后打印});
这里不是同步、异步的问题,而是状态确定了之后,才能进行下一步。
验证中间步骤报错,是否能被最后的逻辑捕获,或者传给下一个 reject。
x
console.log("\n--- 测试 3:错误冒泡 ---");const p3 = new MyPromise((resolve) => { resolve("开始");});p3.then(res => { console.log("P3 第一步:", res); throw new Error("中间环节出错了!");}).then( res => console.log("P3 这一步不应该执行"), err => console.log("P3 捕获到上一步的错误:", err.message));
验证你的 onFulfilledCallbacks 数组是否起作用(多个订阅者是否都能收到通知)。
x
console.log("\n--- 测试 4:多个 then 订阅 ---");const p4 = new MyPromise((resolve) => { setTimeout(() => resolve("数据已更新"), 500);});p4.then(res => console.log("订阅者 A 收到:", res));p4.then(res => console.log("订阅者 B 收到:", res));
这个问题本质在考:
👉 你能不能解释“代码执行顺序”
答案:
中文(面试版本)
Promise 的 then 回调被设计成微任务,是为了保证:
English
then callbacks are executed as microtasks to ensure:
更加深入一点:
假设promise是宏任务:
xxxxxxxxxxsetTimeout(() => console.log("timeout"));Promise.resolve().then(() => console.log("promise"));promise的执行顺序就不稳定了。
Event Loop 执行顺序:
onFulfilled 在同步 resolve 时会立即执行。then 的回调必须在当前代码运行完之后异步执行(即微任务)。我们需要用 queueMicrotask 或 setTimeout(可用来模拟) 来包裹回调,确保执行顺序正确。
面试官要求:
要求 1:异步执行 (Asynchronous Execution)
resolve 了,.then() 里的回调函数也必须在同步代码执行完之后才运行。.then() must execute asynchronously, after the current synchronous code stack is empty.要求 2:微任务优先 (Microtask Priority)
setTimeout(宏任务)。setTimeout (which is a macrotask).分析:
现状问题 (The Issue):
在你目前的代码中,当状态是 fulfilled 时,onFulfilled(this.value) 是立即同步执行的。
xxxxxxxxxx// 你现在的代码逻辑if (this.state === "fulfilled") { onFulfilled(this.value); // 同步执行了!}
改进思路 (The Solution): 我们需要把这行代码包在一个“异步容器”里。
queueMicrotask(() => { ... })。这是原生 Promise 使用的微任务 API。setTimeout(() => { ... }, 0)。虽然它是宏任务,但在手写练习中经常作为降级处理方案。思路其实很简单了,就是then里面的执行语句,也就是handle函数,要使用queueMicrotask这个api来包裹,就可以创建微任务了。
xxxxxxxxxxconst handle = (callback, value) => { queueMicrotask(() => { try { const result = callback(value); if (value instanceof MyPromise) { result.then(resolve, reject); } else { resolve(result); } } catch (err) { reject(err); } });};其余代码都不用改变。
测试用例 (Test Cases)
测试 A:验证执行顺序(面试核心)
目的:验证 Promise 的回调是否在同步代码之后运行。
x
console.log("--- 测试 A 开始 ---");new MyPromise((resolve) => { console.log("1. 构造函数同步执行"); resolve("3. Promise 回调执行");}).then(res => console.log(res));console.log("2. 外部同步代码结束");// 预期顺序:1 -> 2 -> 3
如果没有实现微任务调用,那么执行顺序就是这样的:

测试 B:验证微任务优先级
目的:验证 Promise (微任务) 是否比 setTimeout (宏任务) 先执行。
x
console.log("\n--- 测试 B 开始 ---");setTimeout(() => console.log("2. setTimeout 执行 (宏任务)"), 0);new MyPromise((resolve) => { resolve("1. Promise 执行 (微任务)");}).then(res => console.log(res));// 预期顺序:1 -> 2
测试 C:验证异步链式调用
目的:确保加入微任务逻辑后,异步的 then 链条依然工作正常。
x
console.log("\n--- 测试 C 开始 ---");new MyPromise((resolve) => { setTimeout(() => resolve("Step 1"), 100);}).then(res => { console.log(res); return "Step 2";}).then(res => { console.log(res);});// 预期顺序:100ms 后输出 Step 1,随后输出 Step 2
面试官的要求 (Interviewer's Requirements)
面试官会考察你的代码在“非正常传参”情况下的健壮性:
要求 1:默认回调 (Default Callbacks)
then 没有接收到函数参数(比如 then(null)),它应该将结果原封不动地传给下一个 Promise。then does not receive function arguments (e.g., then(null)), it should pass the result down to the next Promise as-is.要求 2:异常穿透 (Exception Propagation)
then 没传失败回调,错误应该一直向下传递,直到被捕获。then lacks a rejection handler, errors should propagate down until they are caught.分析:
then方法里面有两个参数,一个是onFulfilled,一个是onRejected,都必须是函数才行。
我们应该对两个参数进行判断。then()或者then(1, "failed to post")这类方式的调用,就是非正常传参。then(resolve)或者then(resolve, "hello")就表示onRejected参数非正常的情况;then(null, onRejected)或者then(1, onRejected)就表示onFulfilled参数非正常的情况。这些情况都有可能遇到,那么应该怎么处理呢?
在 then 方法的最开始,检查 onFulfilled 和 onRejected 是否为函数。如果不是,给它们赋值一个“默认转发”函数。
v => v(把值原样返回)。e => { throw e }(把错误原样抛出,以便后续的 catch 捕获)。分析明白了,其实代码就很好改了,也就是从最开始就判断 onFulfilled 和 onRejected 是否为函数,如果不是,就做相应的处理。其余部分都不用改。
xxxxxxxxxxthen(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value; onRejected = typeof onRejected === "function" ? onRejected : reason => {throw reason}; return new Promise((resolve, reject) => { const handle = (callback, value) => { }; }); }测试 1:成功值的连续穿透
目的:验证当连续多个 .then() 都不传参数时,初始的 resolve 值能否安全到达最后一个 .then()。
x
console.log("--- 测试 1:成功值穿透 ---");const p1 = new MyPromise((resolve) => { resolve("核心数据");});p1.then() // 不传参数 .then(null) // 传非函数 .then((data) => { console.log("最终收到的值:", data); // 预期输出: 核心数据 });
测试 2:失败原因的连续穿透(错误冒泡)
目的:验证错误是否能跳过中间没有定义 onRejected 的 then,直到被捕获。
x
console.log("\n--- 测试 2:错误穿透 ---");const p2 = new MyPromise((resolve, reject) => { reject("服务器异常");});p2.then((res) => console.log("这一步不应执行")) .then() .then(null, (err) => { console.log("最终捕获到的错误:", err); // 预期输出: 服务器异常 });
测试 3:穿透后的链式返回
目的:验证穿透发生后,后续的 then 是否依然能正常通过 return 改变 Promise 的值。
x
console.log("\n--- 测试 3:穿透后的正常链式 ---");new MyPromise((resolve) => resolve(10)) .then() // 穿透 10 .then((res) => { console.log("收到穿透值:", res); // 10 return res * 2; }) .then((res) => { console.log("计算后的值:", res); // 20 });
测试 4:异步情况下的穿透
目的:验证在 pending 状态下(异步),值穿透逻辑是否依然有效。
x
console.log("\n--- 测试 4:异步穿透 ---");const p4 = new MyPromise((resolve) => { setTimeout(() => resolve("异步穿透成功"), 500);});p4.then() .then((res) => { console.log("异步等待后的结果:", res); // 预期输出: 异步穿透成功 });
面试官的要求 (Interviewer's Requirements)
面试官会观察你如何处理返回值,特别是边界情况:
要求 1:防止死循环 (Cycle Detection)
then 的回调返回了 then 产生的那个新 Promise,必须抛出 TypeError。then callback returns the same Promise instance created by that then, a TypeError must be thrown to avoid an infinite loop.要求 2:支持 Thenable (Support Thenables)
then 方法),这叫“Thenable 协议”。then method), known as the "Thenable" protocol.分析:
问题所在 (The Issue):
在你之前的代码里,我们直接判断了 result instanceof MyPromise。但有两个缺陷:
result 是否就是当前正在创建的那个 promise2。axios 或原生 Promise 返回的对象(因为它们不是 MyPromise 的实例)。解决办法 (The Solution):
我们需要抽象出一个名为 resolvePromise 的辅助函数,专门负责“拆解”返回值。
要求1很好理解,如果没有办法理解要求2,就暂时不看吧,不过要求2仔细看一下,其实也不难理解。
xxxxxxxxxx// 核心辅助函数:处理 then 的返回值 xfunction resolvePromise(promise2, x, resolve, reject) { // 情况 A:循环引用(自己等自己),抛出错误 if (promise2 === x) { return reject(new TypeError('Chaining cycle detected for promise')); } // 情况 B:x 是对象或函数(可能是 Promise) if (x !== null && (typeof x === 'object' || typeof x === 'function')) { let called = false; // 确保 resolve/reject 只被调用一次 try { let then = x.then; if (typeof then === 'function') { // 如果 x.then 是函数,说明它是 Thenable then.call(x, (y) => { if (called) return; called = true; // 递归解析结果,直到拿到基本类型 resolvePromise(promise2, y, resolve, reject); }, (r) => { if (called) return; called = true; reject(r); }); } else { // x 是个普通对象 resolve(x); } } catch (e) { if (called) return; called = true; reject(e); } } else { // 情况 C:x 是基本类型,直接 resolve resolve(x); }}然后在then方法里面,先拿到new Promise返回的对象,然后使用resolvePromise方法来进行处理。
xxxxxxxxxxthen(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v; onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r }; // 1. 先拿到新创建的 promise 引用 const promise2 = new MyPromise((resolve, reject) => { const handle = (callback, data) => { queueMicrotask(() => { try { const x = callback(data); // 2. 调用核心辅助函数来处理返回值 x resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); }; if (this.state === "fulfilled") { handle(onFulfilled, this.value); } else if (this.state === "rejected") { handle(onRejected, this.reason); } else if (this.state === "pending") { this.onFulfilledCallbacks.push(() => handle(onFulfilled, this.value)); this.onRejectedCallbacks.push(() => handle(onRejected, this.reason)); } }); return promise2;}resolvePromise里面递归的作用:
.then(res => ...) 里的 res 永远是我们要的业务数据,而不是另一个 Promise 实例。如果不递归:当你解析完第一层,发现结果还是一个 Promise,你就直接把它传给下一个 then 了。用户拿到的就是一堆 Promise 对象,还得手动再写一个 .then() 才能拿到值。resolvePromise递归就是为了帮助我们拿到的是值。测试用例 (Test Cases)
测试 1:自引用检测
xxxxxxxxxxconst p1 = new MyPromise(resolve => resolve(1));const p2 = p1.then(() => { return p2; // 报错:检测到死循环});p2.then(null, err => console.log("捕获到错误:", err.message));
测试 2:兼容原生 Promise(Thenable)
xxxxxxxxxxconst p3 = new MyPromise(resolve => resolve(1));p3.then(() => { return Promise.resolve("我是原生 Promise 的数据"); }).then(res => console.log("成功拿到跨库数据:", res));
x
/** * MyPromise 最终版 * 特点:支持微任务、链式调用、值穿透、递归解析结果、常用静态方法 */class MyPromise { constructor(executor) { this.state = "pending"; // 状态:pending, fulfilled, rejected this.value = undefined; // 成功后的值 this.reason = undefined; // 失败的原因 this.onFulfilledCallbacks = []; // 成功回调队列 this.onRejectedCallbacks = []; // 失败回调队列 const resolve = (value) => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; // 状态变更后,异步执行所有成功回调 this.onFulfilledCallbacks.forEach((fn) => fn()); } }; const reject = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; // 状态变更后,异步执行所有失败回调 this.onRejectedCallbacks.forEach((fn) => fn()); } }; try { executor(resolve, reject); } catch (e) { reject(e); // 捕获执行器中的同步错误 } } // --- 核心实例方法 --- then(onFulfilled, onRejected) { // 处理值穿透:如果参数不是函数,则忽略并透传结果 onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v; onRejected = typeof onRejected === "function" ? onRejected : (r) => { throw r }; const promise2 = new MyPromise((resolve, reject) => { const handle = (callback, data) => { // 使用微任务队列执行回调 queueMicrotask(() => { try { const x = callback(data); // 核心:解析返回值 x,决定 promise2 的命运 resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); }; if (this.state === "fulfilled") { handle(onFulfilled, this.value); } else if (this.state === "rejected") { handle(onRejected, this.reason); } else if (this.state === "pending") { // 异步任务,先订阅 this.onFulfilledCallbacks.push(() => handle(onFulfilled, this.value)); this.onRejectedCallbacks.push(() => handle(onRejected, this.reason)); } }); return promise2; } // 捕获失败的快捷方式 catch(onRejected) { return this.then(null, onRejected); } // 无论成功还是失败都会执行 finally(callback) { return this.then( (value) => MyPromise.resolve(callback()).then(() => value), (reason) => MyPromise.resolve(callback()).then(() => { throw reason }) ); } // --- 常用静态方法 --- // 快捷创建成功的 Promise static resolve(value) { if (value instanceof MyPromise) return value; return new MyPromise((resolve) => resolve(value)); } // 快捷创建失败的 Promise static reject(reason) { return new MyPromise((resolve, reject) => reject(reason)); } // 并发控制:全部成功才成功,一个失败就失败 static all(promises) { return new MyPromise((resolve, reject) => { const result = []; let count = 0; const len = promises.length; if (len === 0) return resolve(result); promises.forEach((p, i) => { // 包装一层保证处理非 Promise 对象 MyPromise.resolve(p).then( (val) => { result[i] = val; // 保证顺序一致 count++; if (count === len) resolve(result); }, (err) => reject(err) ); }); }); } // 竞速:谁跑得快就用谁的结果 static race(promises) { return new MyPromise((resolve, reject) => { if (promises.length === 0) return; promises.forEach((p) => { MyPromise.resolve(p).then(resolve, reject); }); }); }}/** * 核心辅助函数:处理 then 回调的返回值 x */function resolvePromise(promise2, x, resolve, reject) { // 1. 循环引用检测 if (promise2 === x) { return reject(new TypeError("Chaining cycle detected for promise")); } // 2. 判断 x 是否为 Promise 或 Thenable 对象 if (x !== null && (typeof x === "object" || typeof x === "function")) { let called = false; // 状态锁定:确保 resolve/reject 只被调用一次 try { let then = x.then; if (typeof then === "function") { // 如果有 then 方法,就当做 Promise 处理。使用call方法,给 then 这个函数传递两个参数函数。 then.call( x, (y) => { if (called) return; called = true; // 递归解析,防止 y 还是一个 Promise resolvePromise(promise2, y, resolve, reject); }, (r) => { if (called) return; called = true; reject(r); } ); } else { // 普通对象 resolve(x); } } catch (e) { if (called) return; called = true; reject(e); } } else { // 3. x 为基本类型,直接成功 resolve(x); }}