关于 JavaScript Promise 的恐怖故事
Source: Dev.to
请提供您希望翻译的文章正文内容,我将为您将其翻译成简体中文,并保持原有的 Markdown 格式、代码块和链接不变。
午夜冒险前 – 什么是 Promise?
“别担心——这比面对床底下的怪物要容易得多!”
Promise 是一个对象,用来表示一次异步操作最终的 完成(或 失败)状态。
现实生活类比:
你的朋友答应给你带咖啡。现在你手里没有咖啡——这个 Promise 处于 pending(待定) 状态。以后你要么收到咖啡(fulfilled 已兑现),要么朋友打电话说咖啡洒了(rejected 已拒绝)。
在 JavaScript 中,Promise 通过 new Promise() 构造函数创建,该构造函数接受一个带有两个参数 resolve 和 reject 的函数。
const coffeePromise = new Promise((resolve, reject) => {
// Simulate making coffee
const success = true;
if (success) {
resolve("☕ Here's your coffee!");
} else {
reject("💔 Spilled the coffee...");
}
});
处理结果
.then()在 Promise fulfilled(已兑现)时执行,并接收 resolve 的值。.catch()在 Promise rejected(已拒绝)时执行,并接收错误信息。
coffeePromise
.then(message => {
console.log(message); // "☕ Here's your coffee!"
})
.catch(error => {
console.log(error); // "💔 Spilled the coffee..."
});
你可以链式调用多个 .then(),但这里先保持简单。
3:34 AM 上厕所之旅 – Promise 类比
现在是凌晨3:34。你的膀胱像 DDoS 攻击一样在尖叫。你需要去卫生间,但:
- 你独居。
- 奶奶的故事说午夜 3 点后会有鬼魂游荡。
- 床下的怪物最活跃。
- 走廊感觉像通往地狱的道路。
你僵住了,权衡两个选项:
- 尿床
- 面对黑暗(超自然存在)
这正好像在 JavaScript 中处理 多个 Promise。
你需要的三个条件
- 打开走廊灯开关 – 希望它能正常工作。
- 打开卫生间灯 – 希望它能正常工作。
- 检查床下 – 希望没有怪物在向你流口水。
// Hallway light
const hallwayLight = new Promise((resolve, reject) => {
const bulbWorks = Math.random() > 0.3; // 70 % chance it works
setTimeout(() => {
if (bulbWorks) {
resolve("💡 Hallway light is ON");
} else {
reject("💀 Hallway light bulb is dead");
}
}, 1000);
});
// Bathroom light
const bathroomLight = new Promise((resolve, reject) => {
const bulbWorks = Math.random() > 0.2; // 80 % chance it works
setTimeout(() => {
if (bulbWorks) {
resolve("🚽 Bathroom light is ON");
} else {
reject("👻 Bathroom light is dead");
}
}, 1500);
});
// Under‑bed check
const underBedCheck = new Promise((resolve, reject) => {
const monsterPresent = Math.random() > 0.6; // 40 % chance of monster
setTimeout(() => {
if (!monsterPresent) {
resolve("🛏️ Under the bed is CLEAR");
} else {
reject("👹 MONSTER under the bed!");
}
}, 2000);
});
1️⃣ Promise.all()
“我只有在全部三件事都发生时才会起床。如果任意失败,我就待在这里。”
Promise.all([hallwayLight, bathroomLight, underBedCheck])
.then(results => {
console.log("🎉 All systems go! Time to sprint!");
console.log(results);
// You get out of bed and run with tears of happiness
})
.catch(error => {
console.log("😭 NOPE. Staying in bed.");
console.log("Reason:", error);
// You wet the bed. Game over.
});
可能的输出
成功
🎉 All systems go! Time to sprint!
['💡 Hallway light is ON', '🚽 Bathroom light is ON', '🛏️ Under the bed is CLEAR']
失败
😭 NOPE. Staying in bed.
Reason: 💀 Hallway light bulb is dead
何时使用 Promise.all()
- 必须在渲染页面之前所有 API 调用都成功。
- 上传多个文件且每个文件都必须完成。
2️⃣ Promise.allSettled()
“让我先检查所有。等我知道三个安全措施的状态后,再根据实际情况决定。”
Promise.allSettled([hallwayLight, bathroomLight, underBedCheck])
.then(results => {
console.log("📋 Let's assess the situation:");
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`✅ Promise ${index + 1}:`, result.value);
} else {
console.log(`❌ Promise ${index + 1}:`, result.reason);
}
});
// Make a decision based on the collected information
});
示例输出
📋 Let's assess the situation:
❌ Promise 1: 💀 Hallway light bulb is dead
✅ Promise 2: 🚽 Bathroom light is ON
✅ Promise 3: 🛏️ Under the bed is CLEAR
何时使用 Promise.allSettled()
- 需要获取每个 Promise 的结果,无论成功还是失败。
- 对一批操作的结果进行日志记录/报告。
3️⃣ Promise.race() (可选 – 原文未出现,但常常一起使用)
“哪个安全措施先解决,就决定我的命运。”
Promise.race([hallwayLight, bathroomLight, underBedCheck])
.then(first => {
console.log("🏁 First result:", first);
})
.catch(error => {
console.log("💥 First error:", error);
});
使用 Promise.race() 的时机
- 实现超时。
- 在多个来源中对最快的响应做出响应。
4️⃣ Promise.any() (可选 – 原文未包含,但完整集合)
“只要 any 项安全措施成功,我就会立即执行;在有一个成功之前,所有失败都会被忽略。”
Promise.any([hallwayLight, bathroomLight, underBedCheck])
.then(firstSuccess => {
console.log("🚀 First successful promise:", firstSuccess);
})
.catch(aggregateError => {
console.log("❗ All promises rejected:", aggregateError);
});
何时使用 Promise.any()
- 尝试多个冗余服务,并在第一个可用的服务上继续。
- 只要有任意一次成功即可的回退策略。
要点
Promise.all→ 必须全部成功。Promise.allSettled→ 了解每个 promise 的结果。Promise.race→ 第一个完成(成功或失败)的决定胜负。Promise.any→ 第一个成功的决定胜负;在有成功之前忽略所有失败。
有了这些工具,你就能应对任何凌晨三点的上厕所之旅——或任何异步 JavaScript 挑战! 🚀
Promise 方法与凌晨 3 点惊魂故事
以下是原始 markdown 的清理版。结构、表情符号和叙事保持不变,但格式已改为符合标准 Markdown 规范。
1️⃣ Promise.all()
Promise.all([hallwayLight, bathroomLight, underBedCheck])
.then((results) => {
console.log('✅ All checks completed:');
results.forEach((result, i) => console.log(` ${i + 1}. ${result}`));
})
.catch((error) => {
console.error('❌ One of the checks failed:', error);
});
何时使用
- 需要 所有 操作都成功后才继续。
- 示例:上传多个文件,只有全部上传成功才继续。
- 示例:批处理任务必须完整完成后才能继续后续工作。
2️⃣ Promise.allSettled()
Promise.allSettled([hallwayLight, bathroomLight, underBedCheck])
.then((results) => {
console.log('📋 Full report of every operation:');
results.forEach((result, i) => {
if (result.status === 'fulfilled') {
console.log(` ${i + 1}. ✅ ${result.value}`);
} else {
console.log(` ${i + 1}. ❌ ${result.reason}`);
}
});
const anyLight = results.some(r =>
r.status === 'fulfilled' &&
(r.value.includes('light') || r.value.includes('CLEAR'))
);
if (anyLight) {
console.log('🏃 Good enough! Making a run for it!');
} else {
console.log('😱 Complete darkness AND monsters? Staying put.');
}
});
何时使用
- 需要获取 每个 Promise 的结果,无论成功还是失败。
- 常见场景:
- 上传多个文件时想知道哪些成功、哪些失败。
- 运行批处理并需要完整的报告。
3️⃣ Promise.race()
Promise.race([hallwayLight, bathroomLight, underBedCheck])
.then((firstResult) => {
console.log(`🏁 First thing happened: ${firstResult}`);
console.log('🚀 Taking that as a sign! Running NOW!');
// You sprint regardless of what the first result was
})
.catch((firstError) => {
console.log(`🏁 First thing happened (and it was bad): ${firstError}`);
console.log('😰 Oh no. That’s a bad sign. But I’m already out of bed!');
// You’re already committed. You run into darkness.
});
何时使用
- 需要 第一个 已定局的 Promise(成功 或 失败)。
- 常见用途:
- 实现超时:
Promise.race([actualRequest, timeoutPromise])。 - 从多个来源中获取最快的响应。
- 实现超时:
4️⃣ Promise.any()
Promise.any([hallwayLight, bathroomLight, underBedCheck])
.then((firstSuccess) => {
console.log(`🎯 Found one working safety measure: ${firstSuccess}`);
console.log('🏃 That’s enough! Making my move!');
})
.catch((error) => {
console.log('💀 ALL safety measures failed!');
console.log('Errors:', error.errors); // AggregateError
console.log('Staying in bed forever.');
});
何时使用
- 只需要 一个 成功的结果,其他可以忽略。
- 常见场景:
- 备用 API(先尝试服务器 A,失败后尝试服务器 B)。
- 任何只要有一次成功就足以继续的情况。
快速回顾
| 方法 | 返回内容 | 何时选择 |
|---|---|---|
Promise.all() | 所有已兑现的值(或在首次失败时拒绝) | 你需要所有都成功。 |
Promise.allSettled() | 每个 promise 的 {status, value/reason} 数组,适用于所有 promise | 你想要完整报告,成功和失败。 |
Promise.race() | 第一个已解决的值 或 拒绝 | 你在乎速度,而不是完整性。 |
Promise.any() | 第一个已兑现的值(如果没有则为 AggregateError) | 只要有一次成功即可。 |
🎮 玩像素艺术游戏!
如果你喜欢这个故事,来看看我为它制作的像素艺术游戏吧。点击下面的截图,选择你的 Promise 方法,尝试在夜晚中生存下来!
欢迎反馈!
如果你发现任何错误或有建议,请告诉我。你可以在 LinkedIn 和 X 上找到我,我在那里分享更多有趣的代码片段。