iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0

嗨大家好,今天要來聊Async / Await。


Async / Await

在講async/await之前,先簡單回顧一下「非同步」這件事。

JavaScript 的程式碼通常是在單一執行緒上執行,但我們常常需要做一些「要等一下才會有結果」的事情,例如:

  • 打API拿資料
  • 讀寫檔案
  • 設定計時器(setTimeout)
  • 查詢資料庫

這些操作如果用「同步」的方式寫,程式就會卡住不動,使用者體驗會很差。所以早期我們會用 Callback(回呼函式) 來處理:

getUser(id, (user) => {
  getOrders(user.id, (orders) => {
    getOrderDetail(orders[0].id, (detail) => {
      console.log(detail);
	    });
	});
});

後來ES6引入了 Promise,改善了Callback的結構,讓程式更簡潔:

getUser(id)
  .then((user) => getOrders(user.id))
  .then((orders) => getOrderDetail(orders[0].id))
  .then((detail) => console.log(detail))
  .catch((err) => console.error(err));

但當邏輯變複雜(例如:加上if/else、迴圈)時,.then() 串接還是會讓程式碼變得不直覺。

於是,Async / Await 就誕生了!它是ES2017(ES8)加入的語法糖,讓我們可以用「看起來像同步」的方式,寫非同步程式碼。

Async / Await 基本語法

  • async:宣告在函式前面,代表這是一個「非同步函式」,它的回傳值一定會被包成一個 Promise。
  • await:只能用在async函式內部,當程式執行到await時,會暫停目前async函式後面的程式碼,等Promise有結果後再繼續執行。

把上面 Promise 的例子改寫成 async/await:

async function getOrderDetailFlow(id: string) {
  const user = await getUser(id);
  const orders = await getOrders(user.id);
  const detail = await getOrderDetail(orders[0].id);
  console.log(detail);
}

TypeScript中的Async / Await

如果你只是寫JavaScript,async/await就已經很好用了;但套用到TypeScript,型別系統會幫你把非同步的「回傳值型別」也顧好。

1. async函式的回傳型別一定是Promise<T>

async function getUserName(id: string): Promise<string> {
  const user = await getUser(id);
  return user.name; // 型別是 string
}

就算你在函式內return user.name,TS也會自動把回傳型別包裝成Promise<string>,因為 async函式本質上就是回傳一個Promise。

2. await會自動解包Promise的型別

async function fetchOrders(): Promise<Order[]> {
  const response = await fetch("/api/orders"); // response: Response
  const data: Order[] = await response.json();  // data: Order[]
  return data;
}

這邊 await的效果,就是把 Promise<Order[]> 轉成 Order[],不用再手動處理 .then() 拆值,型別也會跟著正確推導。

用 try / catch 取代 .catch()

在Promise中,我們用 .catch() 處理錯誤;在async/await的世界,直接用try / catch就可以了:

async function loadUser(id: string) {
  try {
    const user = await getUser(id);
    console.log(user);
  } catch (error) {
    console.error("讀取使用者失敗:", error);
  } finally {
    console.log("不管成功失敗,這裡都會執行");
  }
}

在TypeScript中,catch抓到的error預設型別是unknown,使用前建議先做型別收窄:

catch (error) {
  if (error instanceof Error) {
    console.error(error.message);
	}
}

小結

Async/Await可以說是現在前端開發中很常見的非同步寫法,尤其是在打API、串接後端服務時很常看到。今天的內容就到這邊啦!各位明天見!


上一篇
[Day-23]TypeScript與JavaScript API
下一篇
[Day-25]npm套件
系列文
從零開始學習TypeScript:菜鳥的入門介紹與概覽 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言