如何實現基於 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()

html
<button id="set-alarm">Set alarm</button>
<div id="output"></div>
js
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 函式本身接受兩個引數,這兩個引數也是函式,通常稱為resolvereject。在你的 executor 實現中,你呼叫底層非同步函式。如果非同步函式成功,則呼叫resolve,如果失敗,則呼叫reject。如果 executor 函式丟擲錯誤,則會自動呼叫reject。你可以將任何型別的單個引數傳遞到resolvereject中。

所以我們可以這樣實現alarm()

js
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 的處理程式。

js
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

js
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}`;
  }
});

另請參閱