js 你不知道的那些东西——js 如何实现基于 Promise 的 API

127 阅读2分钟

这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情

如何实现基于 Promise 的 API

在上一篇文章中,我们讨论了如何使用返回 PromiseAPI。在本文中,我们将研究另一面——如何实现返回 PromiseAPI。与使用基于 PromiseAPI 相比,这是一项不太常见的任务,但仍然值得了解。

先决条件: 基本的计算机知识,对 JavaScript 基础知识的合理理解,包括事件处理和 Promise 的基础知识。 客观的: 了解如何实现基于 PromiseAPI。 通常,当您实现基于 PromiseAPI 时,您将包装一个异步操作,该操作可能使用事件、普通回调或消息传递模型。您将安排一个Promise对象正确处理该操作的成功或失败。

实现一个alarm() API 在这个例子中,我们将实现一个基于 Promise 的警报 API,称为alarm(). 它将以要唤醒的人的姓名作为参数,并在唤醒人之前等待以毫秒为单位的延迟。延迟后,函数会发送“Wake up!” 消息,包括我们需要唤醒的人的姓名。

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

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

const output = document.querySelector('#output');
const button = document.querySelector('#set-alarm');

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

button.addEventListener('click', setAlarm);

Promise() 构造函数

当计时器到期时,我们的alarm()函数将返回一个Promise已完成的值。它将通过“唤醒!” 消息进入then()处理程序,如果调用者提供负延迟值,将拒绝承诺。

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

这个 executor 函数本身有两个参数,它们也是函数,通常称为resolveand reject。在您的执行器实现中,您调用底层异步函数。如果异步函数成功,则调用resolve,如果失败,则调用reject。如果执行器函数抛出错误,reject会自动调用。您可以将任何类型的单个参数传递到resolveandreject中。

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

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

此函数创建并返回一个新的Promise. 在 promiseexecutor 内部,我们:

  • 检查这delay不是负数,如果是,则抛出错误。

  • 调用window.setTimeout(),传递回调和delay. 计时器到期时将调用回调,并在我们调用的回调resolve中传递我们的"Wake up!"消息。

    使用alarm() API

    这部分在上一篇文章中应该很熟悉了。我们可以alarm()在返回的 Promise 调用中调用then(), andcatch()来设置处理程序来实现和拒绝承诺。

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');
    }
    window.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”设置一个负值。

通过 alarm() API 使用 asyncawait 由于alarm()返回 a Promise,我们可以用它做任何我们可以用任何其他承诺做的事情:承诺链Promise.all()、 和async/ await

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');
    }
    window.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}`;
  }
});