中文: 实现一个最基础的 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来实现,它语法直观、可读性强。
xxxxxxxxxx31class Promise {2 3}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 的。如果你尝试从外部传入这两个函数,代码会变得逻辑混乱。比如:
xxxxxxxxxx31// 假设按照你设想的“外部传参”方式(这是错误的、不存在的写法)2const myResolve = (val) => { /* 我该怎么修改 p 的内部状态? */ };3const p = new Promise(myResolve, myReject, (res, rej) => { });你会发现 myResolve 根本不知道它要“解决”哪一个 Promise 实例。
总结
在 constructor 内部实现这两个函数,本质上是 Promise 在向你发出邀请:
“嘿,我为你这个实例专门定制了两个改状态的工具,我现在通过
executor的参数交给你。只有你能用它们来告诉我任务结束了,别人都改不了我。”
这就是为什么我们在实现 MyPromise 时,必须在 constructor 内部手动写好 const resolve = ...,然后把这个定义好的函数引用传给 executor。
理解了这个设计,你也就理解了 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接口数据之后做下一步的动作。
xxxxxxxxxx401// 模拟一个请求后端的函数,它返回一个 Promise2function loginApi(username, password) {3 return new Promise((resolve, reject) => {4 console.log("正在连接服务器...");5 6 setTimeout(() => {7 if (username === "admin" && password === "123") {8 resolve({ token: "ABC-123", userId: 1 }); // 登录成功9 } else {10 reject("用户名或密码错误"); // 登录失败11 }12 }, 2000); // 模拟 2 秒的网络延迟13 });14}1516// 实际项目中的逻辑应用17function handleLogin() {18 const btn = { loading: true }; // 按钮进入加载状态19 console.log("按钮开始转圈...");2021 loginApi("admin", "123")22 .then((userData) => {23 // 这里的 userData 就是上面 resolve 传出来的对象24 console.log("登录成功,用户令牌:", userData.token);25 console.log("正在跳转到主页...");26 })27 .catch((error) => {28 // 这里的 error 就是上面 reject 传出来的字符串29 console.error("登录失败提示:", error);30 alert("请检查账号密码!");31 })32 .finally(() => {33 // 无论成功还是失败,最后都要把加载状态关掉34 btn.loading = false;35 console.log("按钮停止转圈。");36 });37}3839// 执行40handleLogin();这个例子可以很好的说明,Promise能够让你不用关心任务什么时候结束(因为用到Promise的地方,可能都会有异步操作),在new的时候告诉它执行什么任务,之后只需要通过 then 告诉它“之前的任务结束之后该干嘛”。
代码如下:
xxxxxxxxxx321class MyPromise {2 constructor(executor) {3 // 状态4 this.state = "pending";5 this.value = undefined;6 this.reason = undefined;78 // 成功的处理函数9 const resolve = (value) => {10 // 防止多次调用 resolve / reject11 if (this.state === "pending") {12 this.state = "fulfilled";13 this.value = value;14 }15 };1617 // 失败的处理函数18 const reject = (reason) => {19 // 防止多次调用 resolve / reject20 if (this.state === "pending") {21 this.state = "rejected";22 this.reason = reason;23 }24 };2526 try {27 executor(resolve, reject);28 } catch (err) {29 reject(err);30 }31 }32}下面是对代码的解释:
- Promise 的定义:它是一个“状态机”
在你的代码里,
class MyPromise就是在定义这个状态机。
作用:Promise 是一个用于表示异步操作结果的对象,它本质是一个状态机 + 回调容器。
Promise是状态机:
this.state = "pending":接水中(还没满,也没漏)。👉 有三种状态:pending / fulfilled / rejected 👉 状态只能改变一次
Promise 保存“未来的结果”,供then来消费
this.value:接满后的那一杯水。this.reason:如果饮水机坏了,坏的原因。
- 什么是 Executor(执行器)?
executor是连接“Promise 内部逻辑”和“外部业务逻辑”的桥梁。
作用:它让你在创建 Promise 的那一刻,就立刻开始执行你的业务(比如开始接水)。
代码体现:
xxxxxxxxxx21// 你的构造函数里这一行最关键2executor(resolve, reject);你把内部写好的两个“开关”函数
resolve和reject传给用户。用户在业务成功时拨动resolve开关,失败时拨动reject开关。
- 用法:如何使用这个最简版?
假设我们要模拟一个同步的业务逻辑(因为你这个版本暂不支持异步):
xxxxxxxxxx171// 1. 创建实例(也就是执行了 constructor)2const myP = new MyPromise((resolve, reject) => {3// 这部分就是 executor4console.log("1. 业务开始执行...");5const success = true;6if (success) {7resolve("这是结果数据"); // 2. 拨动成功开关8} else {9reject("出错了");10}11});1213// 3. 调用 then(也就是执行了 then 方法)14myP.then(15(data) => { console.log("3. 成功回调执行:", data); },16(err) => { console.log("失败回调执行:", err); }17);
结合你的代码看流程
初始化:当你
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函数,并传递相应的参数即可。
xxxxxxxxxx141class MyPromise {2 constructor(executor) {3 4 }5 6 then(onFulfilled, onRejected) {7 if(this.state === "fulfilled") {8 onFulfilled(this.value)9 } 10 if(this.state === "rejected") {11 onRejected(this.reason)12 }13 }14}测试:
针对你实现的那个最简版 MyPromise,我们可以通过以下三个测试案例来验证它的核心逻辑(同步执行、状态锁定、异常捕获)。
你可以将这些代码直接粘贴到你的 MyPromise 类下方运行:
这是最基础的场景,验证 resolve 是否能正常改变状态并触发 then。
xxxxxxxxxx91console.log("--- 测试 1:同步 Resolve ---");2const p1 = new MyPromise((resolve, reject) => {3 resolve("成功数据");4});56p1.then(7 data => console.log("测试 1 成功回调:", data), // 预期输出: 成功数据8 err => console.log("测试 1 失败回调:", err)9);
Promise 的规范要求状态一旦改变就不能再变。这个案例验证你的 if (this.state === "pending") 判断是否生效。
xxxxxxxxxx111console.log("\n--- 测试 2:状态锁定 ---");2const p2 = new MyPromise((resolve, reject) => {3 resolve("第一次成功"); 4 reject("尝试失败"); // 这一行不应该生效5 resolve("第二次成功"); // 这一行也不应该生效6});78p2.then(9 data => console.log("测试 2 结果:", data), // 预期输出: 第一次成功10 err => console.log("测试 2 错误:", err)11);
验证当 executor 内部发生代码错误时,Promise 是否能自动转为 rejected 状态。
xxxxxxxxxx91console.log("\n--- 测试 3:捕获异常 ---");2const p3 = new MyPromise((resolve, reject) => {3 throw new Error("代码报错了!"); // 模拟代码运行崩溃4});56p3.then(7 data => console.log("测试 3 成功:", data),8 err => console.log("测试 3 捕获到错误:", err.message) // 预期输出: 代码报错了!9);
为了让你看清这个版本的局限性,你可以运行下面这个案例。你会发现控制台什么都不会输出:
xxxxxxxxxx101console.log("\n--- 测试 4:异步(会失败) ---");2const p4 = new MyPromise((resolve, reject) => {3 setTimeout(() => {4 resolve("异步数据");5 }, 1000);6});78p4.then(data => console.log("测试 4 异步结果:", data)); 9// 结果:控制台无输出。10// 原因: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.
新增两个队列:
xxxxxxxxxx21this.onFulfilledCallbacks = [];2this.onRejectedCallbacks = [];在then中,处理state为“pending”时的逻辑,也就是store the callbacks。
xxxxxxxxxx141then(onFulfilled, onRejected) {2 if (this.state === "fulfilled") {3 onFulfilled(this.value);4 }56 if (this.state === "rejected") {7 onRejected(this.reason);8 }910 if(this.state === 'pending') {11 this.onFulfilledCallbacks.push(onFulfilled)12 this.onRejectedCallbacks.push(onRejected)13 }14}修改resolve和reject,当异步任务完成后,执行resolve或者reject的时候,将队列里面的callbacks取出来执行。
xxxxxxxxxx211// 成功的处理函数2const resolve = (value) => {3 // 防止多次调用 resolve / reject4 if (this.state === "pending") {5 this.state = "fulfilled";6 this.value = value;7 }89 this.onFulfilledCallbacks.forEach(fn => fn(value))10};1112// 失败的处理函数13const reject = (reason) => {14 // 防止多次调用 resolve / reject15 if (this.state === "pending") {16 this.state = "rejected";17 this.reason = reason;18 }1920 this.onRejectedCallbacks.forEach(fn => fn(reason))21};完整代码:
xxxxxxxxxx541class MyPromise {2 constructor(executor) {3 // 状态4 this.state = "pending";5 this.value = undefined;6 this.reason = undefined;78 this.onFulfilledCallbacks = [];9 this.onRejectedCallbacks = [];1011 // 成功的处理函数12 const resolve = (value) => {13 // 防止多次调用 resolve / reject14 if (this.state === "pending") {15 this.state = "fulfilled";16 this.value = value;17 }1819 this.onFulfilledCallbacks.forEach(fn => fn(value))20 };2122 // 失败的处理函数23 const reject = (reason) => {24 // 防止多次调用 resolve / reject25 if (this.state === "pending") {26 this.state = "rejected";27 this.reason = reason;28 }2930 this.onRejectedCallbacks.forEach(fn => fn(reason))31 };3233 try {34 executor(resolve, reject);35 } catch (err) {36 reject(err);37 }38 }3940 then(onFulfilled, onRejected) {41 if (this.state === "fulfilled") {42 onFulfilled(this.value);43 }4445 if (this.state === "rejected") {46 onRejected(this.reason);47 }4849 if(this.state === 'pending') {50 this.onFulfilledCallbacks.push(onFulfilled)51 this.onRejectedCallbacks.push(onRejected)52 }53 }54}测试:
xxxxxxxxxx81console.log("\n--- 测试 4:异步 ---");2const p4 = new MyPromise((resolve, reject) => {3 setTimeout(() => {4 resolve("异步数据");5 }, 1000);6});78p4.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
xxxxxxxxxx51then(onFulfilled, onRejected) {2 return new MyPromise((resolve, reject) => {3 4 });5}2、需要处理onFulfilled、onRejected函数执行后的返回值。
接收返回值:
xxxxxxxxxx31const result = onFulfilled(this.value);2// 或者3const result = onRejected(this.reason);返回值可能有三种类型:
普通值
xxxxxxxxxx11.then(() => 1)返回值是1,直接当作成功结果处理即可。
xxxxxxxxxx11resolve(1)
返回promise对象(这个是最难理解的)
xxxxxxxxxx11.then(() => Promise.resolve(2))返回值是一个promise对象,接管它的状态。如果promise里面的任务状态变为fulfilled,就交给resolve处理;否则交给reject处理。
resolve和reject本身就是函数。
xxxxxxxxxx11result.then(resolve, reject)
错误
xxxxxxxxxx31.then(() => {2 throw new Error("err")3})使用try...catch来处理,在catch中reject(err)抛出错误。
xxxxxxxxxx51try {2 const result = 3} catch (e) {4 reject(e)5}3、使用instanceof来判断某个对象是否属于Promise的实例
4、如果new Promise()里面的任务是异步任务,当then执行之后,不能简单的将onFulfilled、onRejected方法推送到队列中去,应该对它们进行同样的处理,然后这个then后面的.then().then()能够得到链式调用。
5、疑问:
为什么this.state === 'rejected'了,不直接onRejected(this.reason)执行呢?还要像fulfilled那样继续进行处理呢?
我被rejected的拒绝,这个翻译误导了。这个rejected并不等于是错误,而是一种状态。比如说一个任务,如果做成了接下来就执行一种方案,如果没有做成接下来就执行另外的方案,都是可以继续下去的,而且后面还会有两种方案等着,所以肯定是要继续处理的。
为了避免错误时程序停止执行,那么就需要使用try...catch来捕获错误,供接下来处理。
代码如下:
xxxxxxxxxx921class MyPromise {2 constructor(executor) {3 this.state = "pending";4 this.value = undefined;5 this.reason = undefined;67 this.onFulfilledCallbacks = [];8 this.onRejectedCallbacks = [];910 const resolve = (value) => {11 if (this.state === "pending") {12 this.state = "fulfilled";13 this.value = value;1415 this.onFulfilledCallbacks.forEach((fn) => fn(value));16 }17 };1819 const reject = (reason) => {20 if (this.state === "pending") {21 this.state = "rejected";22 this.reason = reason;2324 this.onRejectedCallbacks.forEach((fn) => fn(reason));25 }26 };2728 try {29 executor(resolve, reject);30 } catch (e) {31 reject(e);32 }33 }3435 then(onFulfilled, onRejected) {36 return new MyPromise((resolve, reject) => {37 if (this.state === "fulfilled") {38 try {39 const result = onFulfilled(this.value);40 if (result instanceof MyPromise) {41 result.then(resolve, reject);42 } else {43 resolve(result);44 }45 } catch (e) {46 reject(e);47 }48 }4950 if (this.state === "rejected") {51 try {52 const result = onRejected(this.reason);53 if (result instanceof MyPromise) {54 result.then(resolve, reject);55 } else {56 resolve(result);57 }58 } catch (e) {59 reject(e);60 }61 }6263 if (this.state === "pending") {64 this.onFulfilledCallbacks.push(() => {65 try {66 const result = onFulfilled(this.value);67 if (result instanceof MyPromise) {68 result.then(resolve, reject);69 } else {70 resolve(result);71 }72 } catch (e) {73 reject(e);74 }75 });7677 this.onRejectedCallbacks.push(() => {78 try {79 const result = onRejected(this.reason);80 if (result instanceof MyPromise) {81 result.then(resolve, reject);82 } else {83 resolve(result);84 }85 } catch (e) {86 reject(e);87 }88 });89 }90 });91 }92}测试:
为了验证你这个版本的 MyPromise(支持异步和链式调用),我们需要设计几个能够体现其“进阶能力”的案例。
请先修正你代码中 reject 方法里的判断 Bug(将 if (this.state === "rejected") 改为 if (this.state === "pending")),然后运行以下测试:
验证 .then() 返回普通值时,能否正确传给下一个 .then()。
xxxxxxxxxx111console.log("--- 测试 1:同步链式调用 ---");2const p1 = new MyPromise((resolve) => {3 resolve(10);4});56p1.then(res => {7 console.log("第一个 then:", res); // 108 return res * 2;9}).then(res => {10 console.log("第二个 then:", res); // 2011});
这是 Promise 最强大的地方:如果第一个 then 返回一个 新的 Promise,第二个 then 是否会等待它完成?
xxxxxxxxxx141console.log("\n--- 测试 2:异步链式调用 ---");2const p2 = new MyPromise((resolve) => {3 setTimeout(() => resolve("第一阶段完成"), 1000);4});56p2.then(res => {7 console.log("P2 第一步:", res);8 // 返回一个新的 Promise9 return new MyPromise((resolve) => {10 setTimeout(() => resolve("第二阶段完成"), 1000);11 });12}).then(res => {13 console.log("P2 第二步:", res); // 应该在第一步 1 秒后打印14});
验证中间步骤报错,是否能被最后的逻辑捕获,或者传给下一个 reject。
xxxxxxxxxx121console.log("\n--- 测试 3:错误冒泡 ---");2const p3 = new MyPromise((resolve) => {3 resolve("开始");4});56p3.then(res => {7 console.log("P3 第一步:", res);8 throw new Error("中间环节出错了!");9}).then(10 res => console.log("P3 这一步不应该执行"),11 err => console.log("P3 捕获到上一步的错误:", err.message)12);
验证你的 onFulfilledCallbacks 数组是否起作用(多个订阅者是否都能收到通知)。
xxxxxxxxxx71console.log("\n--- 测试 4:多个 then 订阅 ---");2const p4 = new MyPromise((resolve) => {3 setTimeout(() => resolve("数据已更新"), 500);4});56p4.then(res => console.log("订阅者 A 收到:", res));7p4.then(res => console.log("订阅者 B 收到:", res));
这个问题本质在考:
👉 你能不能解释“代码执行顺序”
答案:
中文(面试版本)
Promise 的 then 回调被设计成微任务,是为了保证:
English
then callbacks are executed as microtasks to ensure:
更加深入一点:
假设promise是宏任务:
xxxxxxxxxx21setTimeout(() => console.log("timeout"));2Promise.resolve().then(() => console.log("promise"));promise的执行顺序就不稳定了。
Event Loop 执行顺序: