如何实现基于 Promise 的 API

在上一篇文章中,我们讨论了如何使用返回 Promise 的 API。在本文中,我们将探讨另一面——如何实现返回 Promise 的 API。这比使用基于 Promise 的 API 要不常见得多,但仍然值得了解。

先决条件 对 JavaScript 基础知识有合理的理解,包括事件处理和 Promise 的基础知识。
目标 了解如何实现基于 Promise 的 API。

通常,当你实现基于 Promise 的 API 时,你将封装一个异步操作,该操作可能使用事件、普通回调或消息传递模型。你将安排一个Promise对象来正确处理该操作的成功或失败。

实现 alarm() API

在这个示例中,我们将实现一个基于 Promise 的闹钟 API,称为alarm()。它将以要唤醒的人的名字和以毫秒为单位的延迟作为参数,在唤醒该人之前等待。延迟后,该函数将发送一条“Wake up!”消息,其中包括我们需要唤醒的人的名字。

封装 setTimeout()

我们将使用setTimeout() API 来实现我们的alarm()函数。setTimeout() API 以回调函数和以毫秒为单位的延迟作为参数。当调用setTimeout()时,它会启动一个设置为给定延迟的计时器,当时间到期时,它会调用给定的函数。

在下面的示例中,我们使用回调函数和 1000 毫秒的延迟调用setTimeout()

html
<button id="set-alarm">Set alarm</button>
<div id="output"></div>
js
const output = document.querySelector("#output");
const button = document.querySelector("#set-alarm");

function setAlarm() {
  setTimeout(() => {
    output.textContent = "Wake up!";
  }, 1000);
}

button.addEventListener("click", setAlarm);

Promise() 构造函数

我们的alarm()函数将返回一个Promise,当计时器到期时,该Promise将被 fulfilled。它会将“Wake up!”消息传递到then()处理程序中,如果调用者提供负延迟值,则会拒绝该 promise。

这里的主要组件是Promise()构造函数。Promise()构造函数以单个函数作为参数。我们将这个函数称为executor。当你创建一个新的 promise 时,你提供 executor 的实现。

此 executor 函数本身接受两个参数,这两个参数也是函数,通常称为resolvereject。在你的 executor 实现中,你调用底层异步函数。如果异步函数成功,则调用resolve,如果失败,则调用reject。如果 executor 函数抛出错误,则会自动调用reject。你可以将任何类型的单个参数传递到resolvereject中。

所以我们可以这样实现alarm()

js
function alarm(person, delay) {
  return new Promise((resolve, reject) => {
    if (delay < 0) {
      throw new Error("Alarm delay must not be negative");
    }
    setTimeout(() => {
      resolve(`Wake up, ${person}!`);
    }, delay);
  });
}

此函数创建并返回一个新的Promise。在 promise 的 executor 内部,我们

  • 检查delay是否为负数,如果是,则抛出错误。
  • 调用setTimeout(),传递回调和delay。当计时器到期时,将调用回调,并在回调中,我们调用resolve,传入我们的"Wake up!"消息。

使用 alarm() API

这部分应该与上一篇文章中的内容非常相似。我们可以调用alarm(),并在返回的 promise 上调用then()catch()来设置 promise fulfilled 和 rejection 的处理程序。

js
const name = document.querySelector("#name");
const delay = document.querySelector("#delay");
const button = document.querySelector("#set-alarm");
const output = document.querySelector("#output");

function alarm(person, delay) {
  return new Promise((resolve, reject) => {
    if (delay < 0) {
      throw new Error("Alarm delay must not be negative");
    }
    setTimeout(() => {
      resolve(`Wake up, ${person}!`);
    }, delay);
  });
}

button.addEventListener("click", () => {
  alarm(name.value, delay.value)
    .then((message) => (output.textContent = message))
    .catch((error) => (output.textContent = `Couldn't set alarm: ${error}`));
});

尝试为“Name”和“Delay”设置不同的值。尝试为“Delay”设置负值。

使用 async 和 await 与 alarm() API

由于alarm()返回一个Promise,因此我们可以对其执行任何其他 promise 可以执行的操作:promise 链、Promise.all()async/await

js
const name = document.querySelector("#name");
const delay = document.querySelector("#delay");
const button = document.querySelector("#set-alarm");
const output = document.querySelector("#output");

function alarm(person, delay) {
  return new Promise((resolve, reject) => {
    if (delay < 0) {
      throw new Error("Alarm delay must not be negative");
    }
    setTimeout(() => {
      resolve(`Wake up, ${person}!`);
    }, delay);
  });
}

button.addEventListener("click", async () => {
  try {
    const message = await alarm(name.value, delay.value);
    output.textContent = message;
  } catch (error) {
    output.textContent = `Couldn't set alarm: ${error}`;
  }
});

另请参阅