如何实现基于 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()
<button id="set-alarm">Set alarm</button>
<div id="output"></div>
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 函数本身接受两个参数,这两个参数也是函数,通常称为resolve
和reject
。在你的 executor 实现中,你调用底层异步函数。如果异步函数成功,则调用resolve
,如果失败,则调用reject
。如果 executor 函数抛出错误,则会自动调用reject
。你可以将任何类型的单个参数传递到resolve
和reject
中。
所以我们可以这样实现alarm()
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 的处理程序。
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
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}`;
}
});