关于 JavaScript Promise 的恐怖故事

发布: (2026年3月1日 GMT+8 19:11)
10 分钟阅读
原文: Dev.to

Source: Dev.to

请提供您希望翻译的文章正文内容,我将为您将其翻译成简体中文,并保持原有的 Markdown 格式、代码块和链接不变。

午夜冒险前 – 什么是 Promise?

“别担心——这比面对床底下的怪物要容易得多!”

Promise 是一个对象,用来表示一次异步操作最终的 完成(或 失败)状态。

现实生活类比
你的朋友答应给你带咖啡。现在你手里没有咖啡——这个 Promise 处于 pending(待定) 状态。以后你要么收到咖啡(fulfilled 已兑现),要么朋友打电话说咖啡洒了(rejected 已拒绝)。

在 JavaScript 中,Promise 通过 new Promise() 构造函数创建,该构造函数接受一个带有两个参数 resolvereject 的函数。

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 点后会有鬼魂游荡。
  • 床下的怪物最活跃。
  • 走廊感觉像通往地狱的道路。

你僵住了,权衡两个选项:

  1. 尿床
  2. 面对黑暗(超自然存在)

这正好像在 JavaScript 中处理 多个 Promise

你需要的三个条件

  1. 打开走廊灯开关 – 希望它能正常工作。
  2. 打开卫生间灯 – 希望它能正常工作。
  3. 检查床下 – 希望没有怪物在向你流口水。
// 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 方法,尝试在夜晚中生存下来!

欢迎反馈!
如果你发现任何错误或有建议,请告诉我。你可以在 LinkedInX 上找到我,我在那里分享更多有趣的代码片段。

0 浏览
Back to Blog

相关文章

阅读更多 »

第9周:理解异步 JavaScript

Asynchronous JavaScript 第9周的内容是了解 JavaScript 如何处理异步操作:callbacks、promises、async/await 和 event loop。Ca...

必须了解的数组方法

变异方法会更改原数组 - push – 在末尾添加 - pop – 删除末尾元素 - shift – 删除开头元素 - unshift – 在开头添加 所有这些都不会返回新的数组,而是直接修改原数组。