← Writing

JavaScript] - Promise & async/await

繼上篇提及到回呼函式(Callback Function),今天來講 Promise 與 async/await! Promise 是什麼? Promise 是 JavaScript 中用於處理非同步操作的一種概念和對象。 主要目的是更好地處理非同步操作的 Callback Hell (回調地獄)...

javascript

繼上篇提及到回呼函式(Callback Function),今天來講 Promise 與 async/await!

Promise 是什麼?

Promise 是 JavaScript 中用於處理非同步操作的一種概念和對象。 主要目的是更好地處理非同步操作的 Callback Hell (回調地獄) 問題,使代碼更具可讀性和可維護性。

Promise 的三種狀態

  • 待定(Pending):Promise 的初始狀態,表示操作正在進行中,但還未完成。
  • 完成(Fulfilled):表示操作成功完成,執行 resolve 函式,並返回結果。
  • 拒絕(Rejected):表示操作失敗,執行 reject 函式,並返回錯誤信息。

Promise 的主要用途

  • 處理非同步操作: Promise 是處理非同步操作的標準方式,例如發送網絡請求、讀取文件、操作數據庫等。 使得代碼可以在非同步操作完成之後執行相應的操作,而不需要等待。

  • 解決回調地獄問題: 當多個非同步操作依賴於彼此時,使用回調函式可能導致代碼結構深度嵌套, 稱為回調地獄(Callback Hell)。 Promise 可以幫助簡化和清晰化這種情況下的代碼。

  • 序列化非同步操作: 有時需要按特定順序執行一系非同步操作,並且每個操作可能依賴於前一個操作的結果。 Promise 允許你使用 .then() 方法將這些操作鏈接在一起,確保它們按順序執行。

  • 處理錯誤: Promise 具有 .catch() 方法,可以用於捕獲和處理非同步操作中的錯誤,提供更好的錯誤處理機制。

Promise 的重要方法

  • .then():用於處理操作成功的情況,接受一個回呼函式。
  • .catch():用於處理操作失敗的情況,接受一個回呼函式。
  • .finally():無論操作成功還是失敗,都會執行的回呼函式。

.then().catch() 使用情境

想像你正在訂外賣,並希望知道當外賣送達時要做些什麼。

function orderFood() {
  console.log("開始訂外賣...");

  const deliveryTime = Math.random() * 10000; // 模擬外賣送達時間(0 到 10 秒)

  // 使用 Promise 模擬外賣送達
  const deliveryPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = Math.random() > 0.2; // 模擬外賣送達成功或失敗
      if (success) {
        resolve("外賣已送達!");
      } else {
        reject("對不起,配送出了問題。");
      }
    }, deliveryTime);
  });

  // 使用 .then() 處理外賣送達成功情況
  deliveryPromise.then(successMessage => {
    console.log(successMessage);
    console.log("確保你有筷子、餐盤和餐巾紙。");
  });

  // 使用 .catch() 處理外賣送達失敗情況
  deliveryPromise.catch(errorMessage => {
    console.error(errorMessage);
    console.log("不要擔心,你可以聯繫外賣店解決問題。");
  });

  console.log("訂單已下單,等待外賣送達...");
}

// 訂外賣
orderFood();

我們使用 .then() 方法處理成功情況,當外賣成功送達時,我們顯示成功消息並給予一些提示。 當外賣送達失敗時,我們使用 .catch() 方法處理錯誤情況,並提供相應的錯誤消息和建議。

.finally() 使用情境

讓我們通過一個簡單的實際例子來看看 .finally() 的使用情境。

假設你正在開發一個文件上傳功能,用戶可以上傳文件到服務器。 在每次文件上傳之前,你希望顯示一個加載器,無論上傳成功還是失敗,都需要隱藏該加載器。 這時,可以使用 .finally() 來確保不論上傳的結果如何,都會隱藏加載器。

function uploadFile(file) {
  const uploadIndicator = document.getElementById("upload-indicator");
  showLoadingIndicator(uploadIndicator);

  return new Promise((resolve, reject) => {
    // 模擬文件上傳,這裡使用setTimeout模擬非同步操作
    setTimeout(() => {
      const success = Math.random() > 0.5; // 模擬成功或失敗
      if (success) {
        resolve("文件上傳成功");
      } else {
        reject("文件上傳失敗");
      }
    }, 2000);
  })
    .then(result => {
      console.log(result);
    })
    .catch(error => {
      console.error(error);
    })
    .finally(() => {
      hideLoadingIndicator(uploadIndicator);
    });
}

function showLoadingIndicator(indicator) {
  // 顯示加載器
  indicator.style.display = "block";
}

function hideLoadingIndicator(indicator) {
  // 隱藏加載器
  indicator.style.display = "none";
}

const fileInput = document.getElementById("file-input");
fileInput.addEventListener("change", event => {
  const file = event.target.files[0];
  if (file) {
    uploadFile(file);
  }
});

當用戶選擇文件並觸發上傳操作時,我們首先顯示了一個加載器,然後使用 Promise 來模擬文件上傳操作。不論上傳成功還是失敗,.finally() 中的代碼都會確保隱藏加載指示器,從而提供更好的用戶體驗。


async/await 是什麼?

async/await 是 JavaScript 中用於處理非同步操作的一種語法糖, 基於 Promise 提供了更好的非同步操作處理方式,使代碼更易讀且更容易理解。

async/await 的主要概念

  1. async 函式: 使用 async 關鍵字來定義非同步函式,這些函式返回一個 Promise 對象,可以處理非同步操作。

  2. await 關鍵字await 用於等待一個 Promise 完成。當在 async 函式中使用 await 時,該函式會暫停執行,直到等待的 Promise 完成並返回結果。這使得代碼看起來更像同步代碼,因此可以更容易地理解和維護。

  3. 錯誤處理: 使用 try...catch 構造來捕獲非同步操作中的錯誤,就像處理同步代碼一樣。

  4. 多個非同步操作的處理: async/await 可以用於處理多個非同步操作,例如按順序執行多個非同步任務,或並行執行它們,從而使代碼的流程更容易管理。

我們可以使用剛剛外賣訂購的例子來進一步說明 async/await 的用法。 假設你要訂外賣,然後等待外賣送達,然後再享用外賣。

// 模擬外賣送達的函式,返回 Promise
function deliverFood() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = Math.random() > 0.2; // 模擬成功或失敗
      if (success) {
        resolve("外賣已送達!");
      } else {
        reject("對不起,配送出了問題。");
      }
    }, 2000); // 模擬兩秒的送達時間
  });
}

// 定義一個 async 函式,訂外賣並等待送達
async function orderAndAwaitDelivery() {
  try {
    console.log("開始訂外賣...");
    const deliveryResult = await deliverFood();
    console.log(deliveryResult);
    console.log("確保你有筷子、餐盤和餐巾紙。");
  } catch (error) {
    console.error("配送出現問題:", error);
  }
}

// 執行 async 函式
orderAndAwaitDelivery();

deliverFood 函式模擬外賣送達,並返回一個 Promise。 orderAndAwaitDelivery 是一個 async 函式,內部使用 await 等待外賣送達的完成。

當我們執行 orderAndAwaitDelivery 函式時,它首先輸出 "開始訂外賣...",然後等待外賣送達,最後根據送達的結果輸出相應的消息。如果送達過程中出現問題,則捕獲並處理錯誤。


希望這些關於 Promise 和 async/await 的解釋能讓大家更好地理解和應用這兩個重要的非同步操作概念。 在現代 JavaScript 開發中非常常見,用於處理各種非同步任務,提高代碼的效能和可讀性。


參考資料

Promise 是什麼?有什麼用途?