跳轉到

2.1 非同步程式設計#

為什麼 Node 的程式碼「不會等」?#

還記得 1.1 節的事件迴圈嗎?Node 遇到 I/O 不會停下來等,所以你需要一種機制來描述「完成之後要做什麼」。這個機制演進了三代:

callback(回呼) → Promise → async/await(現代標準)

第一代:callback(回呼函式)#

把「完成後要做的事」當成參數傳進去:

const fs = require("node:fs");

fs.readFile("data.txt", "utf8", (err, data) => {
  if (err) {
    console.error("讀檔失敗", err);
    return;
  }
  console.log(data);
});
console.log("這行會先印出來!");  // 不等讀檔,先執行

問題:多個非同步步驟要依序執行時,會出現回呼地獄:

getUser(id, (err, user) => {
  getOrders(user, (err, orders) => {
    getDetails(orders[0], (err, details) => {
      // 縮排越來越深,程式碼變成金字塔
    });
  });
});

第二代:Promise#

Promise 代表「一個未來才會完成的結果」,用 .then 串接:

fetch("https://api.example.com/user")
  .then(res => res.json())
  .then(user => console.log(user))
  .catch(err => console.error(err));   // 錯誤統一處理

第三代:async/await(現代標準,必學)#

用「看起來同步」的寫法處理非同步:

async function main() {
  try {
    const res = await fetch("https://api.example.com/user");
    const user = await res.json();
    console.log(user);
  } catch (err) {
    console.error(err);   // 用熟悉的 try/catch 處理錯誤
  }
}
main();

規則:

  • await 只能用在 async 函式裡
  • await 會「等 Promise 完成」再繼續往下執行
  • 錯誤用 try/catch 接住

平行執行多個非同步工作:用 Promise.all,全部完成才繼續:

const [user, orders] = await Promise.all([
  fetchUser(id),
  fetchOrders(id),
]);

心法:新程式碼一律用 async/await;讀舊程式碼要看得懂 callback 和 .then。