如何实现基于 Promise 的 API
在上篇文章中,我们讨论了如何使用返回 Promise 的 API。在本文中,我们将探讨另一方面——如何实现返回 Promise 的 API。这项任务比使用基于 Promise 的 API 要少见得多,但了解它仍然很有价值。
| 预备知识 | 对本模块前面课程中介绍的 JavaScript 基础知识和异步概念有扎实的理解。 |
|---|---|
| 学习成果 | 了解如何实现基于 Promise 的 API。 |
通常,当你实现一个基于 Promise 的 API 时,你将包装一个异步操作,该操作可能使用事件、普通回调或消息传递模型。你将安排一个 Promise 对象来正确处理该操作的成功或失败。
实现 alarm() API
在本示例中,我们将实现一个名为 alarm() 的基于 Promise 的闹钟 API。它将接收要唤醒的人的名字以及延迟(以毫秒为单位)作为参数。延迟后,该函数将发送一条“该起床了!”消息,其中包括需要唤醒的人的名字。
包装 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() 函数将返回一个在计时器到期时 fulfilled 的 Promise。它将一个“该起床了!”消息传递到 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) {
reject(new Error("Alarm delay must not be negative"));
return;
}
setTimeout(() => {
resolve(`Wake up, ${person}!`);
}, delay);
});
}
此函数创建并返回一个新的 Promise。在 Promise 的 executor 内部,我们
-
检查
delay是否不为负,如果是,则调用reject并传递自定义错误。 -
调用
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) {
reject(new Error("Alarm delay must not be negative"));
return;
}
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) {
reject(new Error("Alarm delay must not be negative"));
return;
}
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}`;
}
});