这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情
如何实现基于 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()功能。API将setTimeout()回调函数和延迟作为参数,以毫秒为单位。当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. 在 promise 的 executor 内部,我们:
-
检查这
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 使用 async 和 await
由于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}`;
}
});