首页 > web前端 > js教程 > 关于 JavaScript Promise 及其工作原理,您需要了解的一切

关于 JavaScript Promise 及其工作原理,您需要了解的一切

Patricia Arquette
发布: 2024-12-17 03:33:25
原创
716 人浏览过

Everything You Need to Know About JavaScript Promises and How They Work

现代 Web 开发严重依赖异步活动来实现响应式、交互式应用程序。无论是从 API 检索数据、读取文件还是运行计时器,这些进程都必须在后台运行,而不会冻结界面。 JavaScript 为您提供了一种可靠的方式来处理这些工作。本文涵盖了您需要了解的有关 Promise 的所有信息,包括基本思想和高级功能,以开发无错误的异步程序。

在本文中您将了解 —

  • 什么是 Promise?

  • 为什么使用 Promise?

  • Promise 如何运作?

  • 处理 Promise

  • 连锁承诺

  • Promise 中的错误处理

  • 高级 Promise 功能

  • 带有 Promise 的 JavaScript 执行流程(重要)

  • 将 Promise 链转换为异步/等待

  • 最佳实践和常见错误

什么是承诺?

JavaScript 中的 Promise 相当于做出“承诺”在未来做某事。当你做出承诺时,你就是在说:“我保证稍后会给你结果。”这个结果可能是成功,也可能是失败。

换句话说,Promise 是一个反映异步操作最终成功(或失败)及其结果值的对象。它允许您将处理程序与异步操作的成功或失败关联起来,使您的代码更易于阅读和维护。

为什么使用 Promise?

例如,在 JavaScript 中,耗时的操作(例如从服务器检索数据)通常是通过回调完成的。回调只是一个传递给另一个函数以在任务完成后执行的函数。例如,您可以使用回调来处理来自服务器的数据。

但是,当有复杂的操作时,回调的使用就变得相当混乱。这种混乱被称为“回调地狱”,其中一个可以在另一个中进行回调,这使得代码不可读且难以管理。

回调地狱示例:

    fetchData((data) => {
      processData(data, (processedData) => {
        saveData(processedData, (result) => {
          console.log(result);
        });
      });
    });
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

如上所示,由于其深层嵌套结构(通常称为“回调地狱”),此类代码在较大的代码库中变得越来越难以阅读和维护。

引入 Promise 来解决这个问题,通过允许以更易读的方式进行链接,提供一种更干净、更有组织的方式来处理异步任务。

基于承诺的方法:

    fetchData((data) => {
      processData(data, (processedData) => {
        saveData(processedData, (result) => {
          console.log(result);
        });
      });
    });
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

这种方法扁平化了结构,使代码更具可读性和可维护性。

承诺如何发挥作用?

JavaScript 中的 Promise 可以处于以下三种状态之一:

  1. 待处理:这是第一步。承诺还没有兑现。

  2. Fulfilled:Promise 已成功完成,这意味着它已解决并且具有价值。

  3. 已拒绝:Promise 未成功完成,并且带有错误消息。

基本语法

fetchData()
  .then(processData)
  .then(saveData)
  .then(console.log)
  .catch(console.error);
登录后复制
登录后复制
登录后复制
登录后复制

在此示例中,promise 在 1 秒后解析,并显示消息“Promise returned!”。 .then() 方法用于处理解析后的值。

处理承诺

使用 .then() 进行成功处理

.then()方法用于处理promise成功完成时发生的情况。它注册函数(回调)以在承诺完成时运行。

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Promise resolved!");
  }, 1000);
});

myPromise.then(result => console.log(result));
登录后复制
登录后复制
登录后复制
登录后复制

使用 .catch() 进行错误处理

.catch()方法用于处理promise失败时发生的情况。它注册一个函数(回调)以在 Promise 被拒绝时运行。

myPromise.then(data => {
  console.log("Data received:", data);
});
登录后复制
登录后复制
登录后复制

使用 .finally() 进行清理

.finally() 方法可以让你在 Promise 完成后运行一些代码,无论成功与否。

myPromise.catch(error => {
  console.error("Error:", error);
});
登录后复制
登录后复制

链接承诺

链接允许您通过传递前一个任务的结果来顺序执行任务。然后继续进行next.then()。这允许您按顺序处理多个异步任务。

链接示例:

myPromise.finally(() => {
  console.log("Cleanup tasks");
});
登录后复制
登录后复制

此示例使用each.then()来处理流程中的每个步骤,从而实现清晰的数据流。这可以让您看到一个阶段的结果如何转移到下一阶段。

Promise 中的错误处理

Promise 通过允许它们将链向下传递给 .catch() 方法来解决,从而简化了错误处理。这消除了在每个阶段处理失败的需要,使您的代码更清晰且更易于管理。

错误传播示例:

fetch('https://api.example.com/user')
  .then(response => response.json())
  .then(data => {
    console.log("Processed data:", data);
    return processData(data);
  })
  .then(finalResult => {
    console.log("Final result:", finalResult);
  })
  .catch(error => console.error("Error:", error));
登录后复制

如果 Promise 链中的任何一步失败,错误将被 .catch() 块捕获。这使得您可以轻松处理问题并保持代码顺利运行。

高级 Promise 功能

1. Promise.all() 用于并行执行

Promise.all() 方法允许您同时运行多个 Promise 并等待它们全部完成。如果所有承诺都得到履行,您将收到每一项承诺的结果。如果任何承诺失败,它会检测到错误。

fetchData()
  .then(processData)
  .then(saveData)
  .catch(error => console.error("An error occurred:", error));
登录后复制

在此示例中,如果任何 Promise 失败,则整个 Promise.all() 都会失败。

2. Promise.race() 实现最快的 Promise

Promise.race() 方法返回第一个完成的 Promise 的结果,无论成功还是失败。

    fetchData((data) => {
      processData(data, (processedData) => {
        saveData(processedData, (result) => {
          console.log(result);
        });
      });
    });
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

在此示例中,无论哪个 Promise(fetchData1 或 fetchData2)先完成,其结果都会记录到控制台。

3. Promise.allSettled() 用于处理所有结果

Promise.allSettled()方法等待你给它的所有promise都处于成功或失败状态,然后完成。然后返回一个数组,其中包含每个承诺的结果。

fetchData()
  .then(processData)
  .then(saveData)
  .then(console.log)
  .catch(console.error);
登录后复制
登录后复制
登录后复制
登录后复制

在此示例中,Promise.allSettled() 等待 fetchData1() 和 fetchData2() 完成。然后它记录每个承诺的状态和结果(或错误)。这样,您就可以看到每个 Promise 发生了什么,无论它们是成功还是失败。

4.Promise.any() 用于解决第一个成功的 Promise

Promise.any() 方法等待 Promise 列表中的第一个 Promise 被正确解析。如果至少有一个 Promise 得到解决,则 Promise.any() 方法将返回该值。如果所有的 Promise 都被拒绝,这个方法将会抛出一个错误。

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Promise resolved!");
  }, 1000);
});

myPromise.then(result => console.log(result));
登录后复制
登录后复制
登录后复制
登录后复制

在此示例中,Promise.any() 等待第一个 Promise 成功解析。该过程返回第一个成功的 Promise 的结果,在本例中,promise2 的值为“Success A”。如果所有承诺都被拒绝,则执行 .catch() 块,并记录错误消息。当您想要收到第一个成功的 Promise 的结果而不必等待其余的结果时,此策略非常有用。

带有 Promise 的 JavaScript 执行流程(重要)

1. JavaScript 中的 Promise 在微任务队列中运行,其优先级高于 setTimeout 等宏任务。

这里有一个例子来说明这一点:

myPromise.then(data => {
  console.log("Data received:", data);
});
登录后复制
登录后复制
登录后复制

在此示例中:

  • console.log(2) 首先运行,因为它是常规同步操作。

  • console.log (6) 接下来运行,因为它也是同步的。

  • promise 的.then() 在 setTimeout 回调之前运行,因为 Promise 是微任务,具有更高的优先级,因此打印 3.

  • 最后,setTimeout 回调运行,因为它是一个宏任务并打印 4。

所以永远记住,由于微任务队列的优先级,promise's.then() 在 setTimeout 回调之前执行。

2. Promise 执行顺序和具有多个 .then() 调用的微任务队列

在 JavaScript 中,代码按特定顺序运行:首先是同步代码,然后是微任务(如 Promise),最后是宏任务(如 setTimeout)。

这里有一个例子来解释这一点:

    fetchData((data) => {
      processData(data, (processedData) => {
        saveData(processedData, (result) => {
          console.log(result);
        });
      });
    });
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

在此示例中,同步代码首先运行,记录 3、6、2、7 和 8。同步代码完成后,将处理微任务(then() 回调),记录 1 和 9。最后,宏任务(来自 setTimeout)按延迟顺序执行,记录 21 (0ms) 和 13 (10ms)。这突出了 JavaScript 的执行顺序:同步代码 >微任务>宏任务。

3. Promise 中的多个 Resolve 和 Reject 调用:只有第一个很重要

当您创建承诺时,第一个解决或拒绝的调用是唯一重要的。所有其他呼叫均被驳回。

这里有一个例子来说明这一点:

fetchData()
  .then(processData)
  .then(saveData)
  .then(console.log)
  .catch(console.error);
登录后复制
登录后复制
登录后复制
登录后复制

在此示例中,promise 使用值 1 进行解析。第二个解析和拒绝调用将被忽略,因为 Promise 已通过第一个解析进行结算。

4. 在连续的 .then() 调用中链接 Promise 并处理值

当你链接 Promise 时,each.then() 会处理流程中的一个步骤。

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Promise resolved!");
  }, 1000);
});

myPromise.then(result => console.log(result));
登录后复制
登录后复制
登录后复制
登录后复制

在此示例中,Promise.resolve(1) 以值 1 开头,但第一个 .then(() => 2) 返回 2。下一个 .then(3) 被忽略,并且值 2 被传递。 .then((value) => value * 3) 将值乘以 3,结果是 6。.then(Promise.resolve(4)) 不会更改值,最后,.then(console. log)logs 6。这演示了值如何通过链传递,非函数值被忽略。

5. 使用 .catch() 和 .finally() 处理的 Promise 链

myPromise.then(data => {
  console.log("Data received:", data);
});
登录后复制
登录后复制
登录后复制

在此示例中,我们将多个 .then()、.catch() 和 .finally() 方法链接在一起,以展示如何处理 Promise 解析的不同阶段。让我们来分解一下:

  • finally() 没有收到参数:
    finally() 块执行清理代码,但不接受或传递任何值。它用于确保某些代码运行,无论承诺的结果如何。

  • 在finally()中返回一个值不会影响promise:
    如果您在finally()块中返回一个值,它不会影响promise链或最终值。它在承诺解决/拒绝后执行,但不会修改结果。

  • 在finally()中抛出错误会导致拒绝:
    如果你在finally()中抛出错误或者返回被拒绝的promise,将会导致promise链被拒绝,并带有错误或者拒绝原因。

myPromise.catch(error => {
  console.error("Error:", error);
});
登录后复制
登录后复制

或者

myPromise.finally(() => {
  console.log("Cleanup tasks");
});
登录后复制
登录后复制
  • then() 和 catch() 的顺序很重要 .then() 和 .catch() 可以按任何顺序调用,但它们将始终返回 Promise 的最终状态。当 .catch() 处理 Promise 时,任何后续的 .then() 将收到最终值。

示例:

    fetchData((data) => {
      processData(data, (processedData) => {
        saveData(processedData, (result) => {
          console.log(result);
        });
      });
    });
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

将 Promise 链转换为 Async/Await

Async/await 是一种使用 Promise 的方法,使代码变得更像以同步模式编写的代码。经常使用的术语是“语法糖”,因为它提供了一种更直接、更清晰的异步代码执行路径。

fetchData()
  .then(processData)
  .then(saveData)
  .then(console.log)
  .catch(console.error);
登录后复制
登录后复制
登录后复制
登录后复制

将 Promise 与 Async/Await 相结合

您可以使用 Promise.all() 将 Promise 与 async/await 结合起来以实现并行执行。

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Promise resolved!");
  }, 1000);
});

myPromise.then(result => console.log(result));
登录后复制
登录后复制
登录后复制
登录后复制

最佳实践和常见错误

  • 避免深层嵌套:使用链接或异步/等待来保持代码平坦和可读。

  • 始终处理错误: 确保每个 Promise 链都有 a.catch() 或 try/catch 块。

  • 明智地使用并行执行:仅当任务独立但需要一起完成时才使用 Promise.all()。

结论

JavaScript Promise 是处理耗时操作(例如在服务器上检索数据)的最佳方法之一。它们甚至可以帮助您编写更清晰、更易于维护的代码,并且您所学知识的实践将使您能够充分利用异步编码。一旦你获得了一些实践经验并开始优雅地处理错误,Promise 将成为 JavaScript 的重要组成部分。

感谢您的阅读!如果您觉得这篇文章有帮助,请随时突出显示、鼓掌、发表评论,甚至在 Twitter/X 和 LinkedIn 上与我联系,因为我非常感激并帮助保持此类内容免费!

以上是关于 JavaScript Promise 及其工作原理,您需要了解的一切的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板