iT邦幫忙

2023 iThome 鐵人賽

DAY 7
1
Software Development

從零開始,在 coding 路上的 30 個為什麼?不對!是無數個為什麼!系列 第 7

Day 07 - 理解 JavaScript ,為什麼要知道 Promise 與 async/await?

  • 分享至 

  • xImage
  •  

繼上篇提及到回呼函式(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 開發中非常常見,用於處理各種非同步任務,提高代碼的效能和可讀性。

下篇想來談談 Event Loop (瀏覽器中的事件循環)!

是不是開始好奇 Event Loop 了呢!為什麼要理解 Event Loop?

下篇待續!

文章同步於個人部落格:Viiisit!(歡迎參觀 ୧ʕ•̀ᴥ•́ʔ୨)


上一篇
Day 06 - 理解 JavaScript ,為什麼要知道一級函式、回呼函式?
下一篇
Day 08 - 理解 JavaScript ,為什麼要知道 Event Loop 事件循環?
系列文
從零開始,在 coding 路上的 30 個為什麼?不對!是無數個為什麼!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言