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