JavaScript Promises - 像我五岁一样解释

2025-06-04

JavaScript Promises - 像我五岁一样解释

如果您觉得这篇文章对您有帮助,那么您很可能也会觉得我的推文对您有所帮助。所以,请务必在Twitter上关注我,以获取更多关于 Web 开发和内容创作的信息。本文最初发表在我的博客上。


大家好👋,欢迎阅读我的全新系列文章《揭秘 JavaScript Promises——一种新的学习方式》。JavaScriptpromises非常特别。作为 Web 开发者,我们几乎无法避免学习它。相信我,即使你不相信,你的面试官也确实喜欢 Promises 😉!

另一方面,如果我们就“最难处理的 JavaScript 概念是什么? ”进行一项民意调查,你会发现“promise”正逐渐跻身榜首。你不相信吗?这是最近的民意调查结果 🙂。

在 LinkedIn 上

LinkedIn 投票.png

在推特上

Twitter 投票.png

因此,它无疑成为了promises讨论的“主题”。在本系列中,你将学习 JavaScript,Promises从入门到高级。我们将涵盖:

  • 是什么Promise以及它的特点是什么?
  • Promise Chain并举例说明。
  • 如何处理errorsPromises 中的问题?
  • Mistakes您可以使用 Promises 来做出承诺。
  • 如何为你的(承诺)做准备interviews

本文将主要以初学者友好的方式介绍对 JavaScript promise 及其特性的基本理解。

杰克和吉尔的故事

杰克和吉尔上山了…… ”童谣有两个主要人物:小男孩杰克和他的妹妹吉尔。让我们来改编一下这个故事,介绍一下他们的祖父母。

于是,杰克和吉尔带promise他们的祖父母fetch去山顶的井里打了一些水。他们开始行动去打水。与此同时,祖父母们正忙着讨论日常生活,他们想等孩子们打完水回来就开始做饭。

杰克和吉尔的故事
💡上面的插图是我根据著名的杰克和吉尔童谣即兴创作的。如有任何与世间任何事物相似之处,纯属巧合。🙂

现在有两种可能性,

  • 杰克和吉尔带着水下来,开始做饭。
  • “杰克摔倒了,摔碎了王冠。吉尔也跟着摔倒了。”——在这种情况下,杰克和吉尔回来了,但不幸的是,他们没有得到水。

在这个短篇故事中,有一个promise关于如何取水的活动。孩子们可以履行这个承诺(取水),也可以因为灾难而拒绝。请注意,当杰克和吉尔在履行executing承诺时,祖父母们并没有闲着。他们正在计划这一天。

JavaScript 的 Promise 也类似。作为开发者,我们创建它们是为了获取某些数据(来自数据存储、配置等等)。通常,获取操作可能不会立即发生。我们希望异步获取数据。这意味着我们不希望应用程序等待响应,而是在响应可用时继续处理。

因此我们的类比表可能看起来像这样,

现实生活中(使用 JavaScript) 在我们的故事中
承诺 杰克👦和吉尔👧取水
执行器函数 取水🏃‍♀️🏃‍♂️
活动 获取🧶
响应中的预期数据 水💧
消费者 祖父母👵👴
解决/实现 ✔️成功获取烹饪用水
拒绝/已拒绝 ❌ 取水灾难(错误)
获取数据成功后的任务 烹饪🍚

如果某些术语对您来说看起来陌生或令人困惑,请不要担心。我们将在本文末尾重新讨论。

JavaScript 中的 Promise

Apromise是一个 JavaScript 对象,允许你进行异步(又称 async)调用。当异步操作成功完成时,它会返回一个值;如果未完成,则会返回一个错误。

您可以使用构造函数方法创建承诺,

let promise = new Promise(function(resolve, reject) {    
    // Do something and either resolve or reject
});
Enter fullscreen mode Exit fullscreen mode

我们需要将一个函数传递给Promise Constructor。这个函数叫做executor function(还记得打水吗?)。执行器函数接受两个参数,resolvereject。这两个是执行器用来宣布结果的回调函数。

resolve方法表示任务成功完成(取水),而reject则表示发生错误(灾难)。您无需实现 resolve/reject 方法。JavaScript 会提供这些方法。您需要在执行器函数中调用它们。

因此,在杰克和吉尔的故事中,该executor函数可能如下所示,

  • 例如resolve
 let promise = new Promise(function(resolve, reject) {
      // Got the water
      let value = 'water';
      resolve(value); // An assurance of getting the water successfully
 });
Enter fullscreen mode Exit fullscreen mode
  • 例如reject
 let promise = new Promise(function(resolve, reject) {
      // OOPS, Jack fell down and broke his crown. 
      // And Jill came tumbling after.
      reject(new Error("Disaster")); // Throwing and error
 });
Enter fullscreen mode Exit fullscreen mode

Promise 对象和状态

在杰克和吉尔的故事中,祖父母并没有等着孩子们去取水。他们一边计划着当天的安排,一边在计划着。无论成功取水还是遭遇灾难,杰克和吉尔都会告知他们。而且,祖父母也是用水来做饭的人。

类似地,承诺对象应该能够在执行开始、完成(解决)或返回错误(拒绝)时通知消费者。

承诺对象具有以下内部属性,

  1. state:此属性可以具有以下值,
    • pending:执行函数启动时。在我们的故事中,就是杰克和吉尔开始打水的时候。
    • fulfilled:当 Promise 成功解决时。例如,杰克和吉尔回到了水边。
    • 拒绝:当承诺被拒绝时。例如,杰克和吉尔无法完成任务。
  2. result:此属性可以具有以下值,
    • undefined:最初,当状态值为 时pending
    • value:当承诺被解决时(值)。
    • error:当承诺被拒绝时。

已解决或已拒绝的承诺称为已解决的承诺。

状态2.png

因此消费者(如祖父母)需要依靠promise对象来了解状态和价值/错误。

处理消费者的承诺

promise构造函数返回的对象包含new Promise所有内容。消费者可以使用它来了解state(待处理、已完成或已拒绝)以及可能的结果(valueerror)。

但是等等。这些属性是内部的。它们无法通过代码访问,但可以检查。这意味着我们可以使用调试工具检查stateresult属性值,但无法直接使用程序访问它们。

那么呢?这就是我们有三个重要的处理程序方法,.then().catch()和。当 Promise 解析或拒绝时,.finally()这些方法帮助我们在 和消费者之间建立链接。executor

执行器-消费者.png

.then() Promise 处理器

每个 Promise都会有一个.then()方法。该方法的唯一目的是让消费者了解 Promise 的结果。它接受两个函数作为参数:resulterror

promise.then(
  (result) => { 
     console.log(result);
  },
  (error) => { 
     console.log(error);
  }
);
Enter fullscreen mode Exit fullscreen mode

如果您只对成功的结果感兴趣,您可以选择只传递一个参数,

promise.then(
  (result) => { 
      console.log(result);
  }
);
Enter fullscreen mode Exit fullscreen mode

类似地,如果您只对错误感兴趣,请将其null作为第一个参数的值传递。

promise.then(
  null,
  (error) => { 
      console.log(error)
  }
);
Enter fullscreen mode Exit fullscreen mode

在错误情况下显式传递 a 的语法有点奇怪null。因此,我们有一个替代方案,称为.catch()方法,我们很快就会看到。

还要注意,你可以在该.then()方法中做三件非常特殊的事情,

  • 您可以从中得到return另一个。promise
  • 您可以return包含一个值undefined
  • 您可能会throw犯错误。

这三点将作为后续文章学习的基础Promise Chain。现在,让我们为杰克和吉尔编写代码,履行给祖父母取水的承诺。

// 1. Create a Promise to fetch the water
let promise = new Promise(function(resolve, reject) {
 // Pretend a delay of 2 sec to fetch it!
  setTimeout(function() {
      // Fetched the water. Let's resolve the promise
      resolve('Hurray! Fetched the Water.');
  }, 2000);
});

// 2. Function to Set up the handler to handle a promise result.
// It is to inform the grandparents when the result is available.
const grandParentsCooking = () => {
  // The handler function to handle the resolved promise
  promise.then(function(result) {
    // Fetched the water. Now grandparents can start the cooking
    console.log(`cooking rice with the ${result}`);
  });
}

// 3. Calling the function to activate the set up.
grandParentsCooking();
Enter fullscreen mode Exit fullscreen mode

输出,

cooking rice with the Hurray! Fetched the Water.
Enter fullscreen mode Exit fullscreen mode

因此,上面的代码中发生了三件事,

  1. 我们创建了 Promise。在执行器函数中,我们延迟 2 秒来模拟异步调用(实际上,爬山和取水需要更多时间!)。然后我们解析 Promise,并返回“好耶!取到水了!”

  2. 我们设置了一个信息机制,让祖父母们知道何时取水成功。我们使用了.then()处理程序来实现这个目的。一旦他们取到水,他们就开始做饭。注意,这里我们只是定义了它,而不是调用它。

  3. 通过调用函数来激活处理程序。

.catch() Promise 处理器

你可以使用这个处理程序方法来处理 Promise 的错误(拒绝)。正如我们之前讨论过的,用这个处理程序来处理错误比用方法处理要好得多.then()。所以现在让我们用 JavaScript Promise 来处理“杰克摔倒了……”的情况。

// 1. Create the promise
let promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
      // Reject it as the disaster happend.
      reject(new Error('Jack fell down and broke his crown. And Jill came tumbling after.'));
  }, 2000);
});

// 2. Inform grandparents 
// but this time we are using the .catch
const grandParentsCooking = () => {
  promise.catch(function(error) {
    console.error(`OMG ${error.message}`);
  });
}

// 3. Call the function
grandParentsCooking();
Enter fullscreen mode Exit fullscreen mode

输出,

图像.png

需要注意的几点

  • 我们使用reject上面代码中的方法来拒绝这个承诺。
  • reject你可以像方法一样向方法传递任何类型的参数resolve。不过,建议使用Error对象。我们将在后续关于使用 Promise 进行错误处理的文章中详细讨论。
  • 我们使用.catch()Handler 来处理拒绝。在实际应用中,你会同时使用.then().catch()方法来处理 resolve 和 rejection 的情况。我们将在本系列的 Promise 链式调用文章中学习它。

.finally() Promise 处理器

handler方法.finally()执行清理操作,例如停止加载器、关闭活动连接等等。无论 Promise 被 resolve 还是 rejection,该.finally()方法都会被调用。

let loading = true;
loading && console.log('Loading...');

// Getting the promise
promise = getPromise();

promise.finally(() => {
    loading = false;
    console.log(`Promise Settled and loading is ${loading}`);
}).then((result) => {
    console.log({result});
});
Enter fullscreen mode Exit fullscreen mode

需要注意的关键点是,该.finally()方法会将结果或错误传递给下一个处理程序,该处理程序可以再次调用.then().catch()。这很方便,我们将在 Promise 链文章中看到很多这样的例子。

总之

总而言之,

  • Promise是 JavaScript 中异步概念的重要组成部分。
  • 您可以使用构造函数创建一个承诺。
  • 构造函数接受执行函数作为参数并返回承诺对象。
  • 对象promise有两个内部属性:state 和 result。这些属性无法通过代码访问。
  • 承诺的消费者可以使用.then().catch().finally()方法来处理承诺。
  • 通过杰克和吉尔的故事等例子可以更好地理解承诺

我希望现在您能够更好地理解类比表。

现实生活中(使用 JavaScript) 在我们的故事中
承诺 杰克👦和吉尔👧取水
执行器函数 取水🏃‍♀️🏃‍♂️
活动 获取🧶
响应中的预期数据 水💧
消费者 祖父母👵👴
解决/实现 ✔️成功获取烹饪用水
拒绝/已拒绝 ❌ 取水灾难(错误)
获取数据成功后的任务 烹饪🍚

暂时就这些。请继续关注本系列的第二篇文章。我们将Promise Chain通过另一个故事来了解。



希望你喜欢这篇文章,或者觉得它对你有帮助。欢迎在Twitter 上关注我 (@tapasadhikary),分享我的想法、技巧和代码实践。

您可能还喜欢,

文章来源:https://dev.to/atapas/javascript-promises-explain-like-im- Five-525g
PREV
JavaScript 中的纯函数和副作用是什么?
NEXT
DevTools - 我最喜欢的技巧和窍门元素面板控制台面板