这篇文章最初是作者在学习 JavaScript 课程时的笔记。
先看课程里的原版类比:
Promise 就像在餐厅点餐:你会拿到一张小票(Promise 对象),表示你的订单正在处理中。订单要么成功送达(resolved,已兑现),要么被取消(rejected,已拒绝),你可以挂上处理动作(
.then()和.catch())来应对这两种结果。
作者把这个类比改了一点、补充了更多细节,然后让 AI 补全。下面是最终成品——用麦当劳点餐来理解 Promise:
-
在麦当劳下单,拿到一个叫号器(buzzer)。
- 这就是创建一个新的 Promise——它代表一个未来的值。
- 叫号器(Promise)此刻处于 pending(等待中) 状态。
-
等待备餐在后台进行。
- 这是一个异步操作,就像
setTimeout()或一次 API 调用。 - 后厨员工(JavaScript 引擎)在帮你做餐,同时不阻塞其他顾客(其他代码照常运行)。
- 这是一个异步操作,就像
-
餐做好时,店员按下按钮,你的叫号器响了。
- 这是 Promise 状态变为 fulfilled(已兑现)。
- 处理异步操作的代码调用了
resolve()函数。
-
做好的餐在你回到柜台时递给你。
- 这就是 Promise 兑现时携带的数据。
- 传给
resolve()的值可以在.then()处理函数里拿到。
-
如果薯条卖完了或者订单出了问题,经理会过来跟你解释。
- 这是 Promise 状态变为 rejected(已拒绝)。
reject()函数被调用,并携带一个错误原因。
-
对问题的解释(比如「薯条卖完了」)就是错误信息。
- 这是 Promise 拒绝时携带的错误数据。
- 传给
reject()的值可以在.catch()处理函数里拿到。
-
你决定下一步怎么办——拿到餐,或者听完问题后换别的。
- 这就是你的代码在
.then()或.catch()处理函数里执行。 - 你可能满意地享用,也可能改点别的。
- 这就是你的代码在
-
当你拿到餐或处理完问题后,你与这个订单的交互就结束了。
- Promise 现在处于 settled(已定型) 状态(fulfilled 或 rejected 二选一)。
- Promise 一旦定型,状态就再也不能改变。
这个类比覆盖了 Promise 的全部三种状态:
- Pending(等待中):已下单,等餐。
- Fulfilled(已兑现):餐做好并交付。
- Rejected(已拒绝):订单因问题无法完成。
代码形式
const orderFood = menuItem => {
return new Promise((resolve, reject) => {
console.log(`Order for ${menuItem} received. Please wait...`);
// Simulate food preparation (async operation)
setTimeout(() => {
// Randomly succeed or fail the order
if (Math.random() > 0.2) {
// Food prepared successfully
const food = `🍔 Fresh ${menuItem}`;
resolve(food); // Promise fulfilled
} else {
// Something went wrong
reject(`Sorry, we're out of ${menuItem}`); // Promise rejected
}
}, 2000);
});
};
// Using the Promise
orderFood('fries')
.then(food => {
console.log(`Enjoy your ${food}!`);
})
.catch(problem => {
console.log(`Order failed: ${problem}`);
})
.finally(() => {
console.log("Thank you for visiting McDonald's");
});
执行流程拆解
-
调用
orderFood('fries')时:- 函数立即执行。
- 创建了一个新的 Promise 对象。
- executor 函数(传给 Promise 构造器的那个函数)同步运行。
-
在 executor 函数内部:
console.log(...)立即执行。- 调用
setTimeout(),把回调安排到稍后运行。 - Promise 对象返回给调用方(仍处于 pending 状态)。
-
Promise 返回之后:
.then()和.catch()处理函数挂到 Promise 上。- 这些处理函数此刻不会执行——它们只是注册好,等稍后运行。
-
定时器完成时(2000ms 后):
- 定时器回调执行。
resolve()或reject()被调用。- Promise 状态变为 fulfilled 或 rejected。
- 对应的处理函数(
.then()或.catch())被排进下一个事件循环迭代执行。
译者注:原文此处有两张示意图(Promise 兑现时 / Promise 拒绝时的控制台输出),爬取时未抓取到图片。
附记:resolve 和 reject 到底是什么?
作者最困惑的一点:resolve 和 reject 这两个函数是哪来的?定义在哪里?
笔记:
resolve 和 reject 只是我们给这两个参数起的常用名字,跟任何函数的参数一样,叫别的名字也行。
这两个函数是 JavaScript 引擎在你创建新 Promise 时提供的——更准确地说,是 Promise 类的 constructor() 提供的。它们会被自动传给 executor 函数(你传给 Promise 构造器的那个函数)。
Promise 构造器的签名长这样:
new Promise(executor);
其中 executor 是这样一个函数:
function executor(resolveFunction, rejectFunction) {
// Your code here
}
resolve 和 reject 是 JavaScript 引擎提供的内部函数。虽然你看不到它们的真实实现,但概念上 resolve 函数会:
- 把 Promise 的状态从 “pending” 改为 “fulfilled”;
- 把传给它的值设为 Promise 的结果值;
- 安排所有挂着的
.then()回调在下一个事件循环迭代执行。
另一个例子
Promise 构造器会给 executor 函数提供两个参数。用其中一个(当作 resolve)还是两个都用(resolve 和 reject),由你自己决定。
const wait = milliseconds => {
return new Promise((resolve, reject) => {
// Validate input
if (typeof milliseconds !== 'number' || milliseconds < 0) {
reject(new Error('milliseconds must be a positive number'));
return;
}
// Set up the timer
setTimeout(() => {
resolve('Timer completed!'); // Can pass a value
}, milliseconds);
});
}
// Usage
wait(1000)
.then(message => console.log(message)) // "Timer completed!"
.catch(error => console.error(error));
可以给 resolve() 传一个值,这个值会在 .then() 回调里拿到。
当你调用 new Promise() 时,JavaScript 引擎会:
- 创建一个处于 “pending” 状态的 Promise 对象;
- 创建两个特殊函数(通常叫
resolve和reject); - 把这两个函数作为参数调用你的 executor 函数;
- 当你调用
resolve()时,触发 Promise 内部的状态变更。