如何實現基於 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}`;
}
});