想要实现基于 promise 的 API ,我们可以使用事件、普通回调或者消息传递模型来包裹一个异步操作。使用 Promise 对象可以合理的处理操作的成功或者失败。
实现 alarm() API
实现一个基于 promise 的 alarm API,叫做 alarm() 。接受两个参数,一个是 人名 ,一个是 延迟时间 。在延迟之后,函数会发送一个包含人名的 “Wake up!” 消息。
用 setTimeout() 包裹
用 setTimeout() 来实现 alarm() 函数。
setTimeout() 接受两个参数,一个是回调函数,一个是延迟时间,当调用 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() {
window.setTimeout(() => {
output.textContent = "Wake up!";
}, 1000);
}
button.addEventListener("click", setAlarm);
控制台输出效果:
Wake up!
Promise() 构造器
设置一个 alarm() 函数返回一个在定时器过期时才会被兑现的 Promise。传递一个 “Wake up!” 消息到 then() 处理器中,也会在当调用者提供一个负延迟值时拒绝这个 promise。
这里的关键组件是 Promise() 构造器。Promise() 构造器使用单个函数作为参数。我们把这个函数称作执行器(executor)。当你创建一个新的 promise 的时候你需要实现这个执行器。
这个执行器接收两个参数,这两个参数都是函数,通常被称作 resolve 和 reject。在执行器实现里,调用原始的异步函数。如果异步函数成功了,就调用 resolve,如果失败了,就调用 reject。如果执行器函数抛出了一个错误,reject 会被自动调用。你可以将任何类型的单个参数传递到 resolve 和 reject 中。
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,我们:
- 检查 delay(延迟)是否为负数,如果是的话就抛出一个错误。
- 调用 window.setTimeout(),传递一个回调函数和 delay(延迟)。当计时器过期时回调会被调用,在回调函数内,我们调用了 resolve,并且传递了 “Wake up!” 消息。
使用 alarm() API
调用 alarm(),在返回的 promise 中调用 then() 和 catch() 来设置 promise 兑现和拒绝状态的处理器。
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}`));
});
控制台输出:
正常:Wake up, Matilda!
给 Delay 设置负值: Couldn’t set alarm: Error: Alarm delay must not be negative
在 alarm() API 上使用 async 和 await
alarm() 返回了一个 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");
}
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}`;
}
});