iT邦幫忙

2025 iThome 鐵人賽

DAY 17
0
Modern Web

Web Bluetooth API 實戰:30 天打造通用 BLE 偵錯工具系列 第 17

Day 17:終極武器 await:用同步思維,駕馭非同步探索流程

  • 分享至 

  • xImage
  •  

昨天,我們深入探索了 async 關鍵字。我們揭開了它的核心秘密:它是一個「Promise 製造機」,能將任何函式都轉變為一個承諾未來的容器,並為我們創造了一個可以處理非同步操作的特殊「異次元空間」。

今天,我們將學習 async 的最佳拍檔,也是這個異次元空間中唯一的「時間暫停」魔法——await 關鍵字。

如果說 async 只是打開了通往非同步世界的大門,那麼 await 就是我們在這片新世界中,用來馴服時間猛獸、駕馭複雜流程的終極武器。它有一種神奇的魔力,可以讓我們用最符合直覺的「同步思維」,來編寫清晰、優雅的「非同步程式碼」。

今天的目標,是徹底掌握 await 的用法,並學會搭配 try...catch 來建立健壯、無懈可擊的非同步程式碼。學完今天,你就擁有了解析複雜 BLE 探索流程所需的全套技能。


1. await 的核心功能:等待與解包

await 這個詞的字面意思就是「等待」。在 async 函式中,它的作用也正是如此,但它有兩個強大的核心功能:

  1. 暫停執行 (Pause Execution)

    • async 函式執行到 await 這一行時,它會暫停下來,但僅僅是這個 async 函式本身暫停,整個網頁的其他部分(如 UI 互動、動畫)依然流暢運行。

    • 它會耐心等待 await 後面的那個 Promise 完成(無論是 fulfilled 還是 rejected)。

  2. 解開承諾 (Unwrap the Promise)

    • 這就是 await 最神奇的地方!如果 Promise 成功 (fulfilled),await 會像拆禮物一樣,直接把 Promise 內部包裝的結果值取出來,交給你。

    • 如果 Promise 失敗 (rejected),await 則會像觸發了警報一樣,將這個錯誤拋出

await 的黃金法則(再次強調):

await 只能在 async 函式內部使用。


2. 實戰演練:一個多步驟的非同步任務

讓我們模擬一個需要多個連續步驟才能完成的非同步任務:1. 獲取使用者資料 -> 2. 檢查權限 -> 3. 載入對應數據。

模擬的非同步函式


// 步驟 1: 模擬獲取使用者,1秒後回傳使用者物件
function fetchUser(userId) {
  return new Promise(resolve => {
    console.log(`(1) Fetching user ${userId}...`);
    setTimeout(() => resolve({ id: userId, name: 'Alice' }), 1000);
  });
}

// 步驟 2: 模擬檢查權限,1秒後回傳權限狀態
function checkPermissions(user) {
  return new Promise(resolve => {
    console.log(`(2) Checking permissions for ${user.name}...`);
    setTimeout(() => resolve(true), 1000);
  });
}

// 步驟 3: 模擬載入數據,1秒後回傳數據或拋出錯誤
function loadData(hasPermission) {
  return new Promise((resolve, reject) => {
    console.log('(3) Loading data...');
    if (!hasPermission) {
      // 如果沒有權限,就讓 Promise 失敗
      return reject(new Error('Permission denied!'));
    }
    setTimeout(() => resolve({ content: 'Secret data...' }), 1000);
  });
}

寫法對比

A. 傳統 .then() 鏈寫法


// fetchUser(1)
//   .then(user => {
//     return checkPermissions(user);
//   })
//   .then(hasPermission => {
//     return loadData(hasPermission);
//   })
//   .then(data => {
//     console.log('Success (then):', data.content);
//   })
//   .catch(error => {
//     console.error('Failed (then):', error.message);
//   });

這種寫法還可以,但層級和回呼函式讓它讀起來有點繞。

B. async/await 終極武器寫法


async function main() {
  try {
    const user = await fetchUser(1);
    const hasPermission = await checkPermissions(user);
    const data = await loadData(hasPermission);

    // 程式碼能走到這裡,代表上面每一步 await 都成功了!
    console.log('Success (await):', data.content);

  } catch (error) {
    // 如果上面任何一個 await 的 Promise 失敗了,
    // 程式碼會立刻跳到這裡來!
    console.error('Failed (await):', error.message);
  }
}

main();

看到差別了嗎?async/await 版本的程式碼,讀起來就跟普通的同步程式碼一模一樣!它的邏輯清晰、從上到下,完全符合我們的思考習慣。這就是它被稱為「終極武器」的原因。


3. try...catchawait 必備的安全網

你可能已經註意到,在 async/await 的寫法中,try...catch 語法塊是多麼重要。

為什麼它是必備的? 因為當 await 等待的 Promise 變成 rejected 狀態時,await 會將這個拒絕的原因(通常是一個 Error 物件)當作錯誤直接拋出 (throw)

如果沒有 try...catch 會發生什麼?

async function riskyOperation() {
  console.log('Trying something that might fail...');
  // 假設這個 Promise 會失敗
  const result = await Promise.reject(new Error('Boom!'));
  // 因為上面一行拋出了錯誤,這行永遠不會執行
  console.log('This will never be logged.');
}

// 執行 riskyOperation();
// 你會在 Console 看到一個紅色的 "Uncaught (in promise) Error: Boom!"
// 這代表錯誤沒有被處理,在真實應用中可能導致程式中斷!

有了 try...catch 才是專業的寫法

async function safeOperation() {
  try {
    console.log('Trying something that might fail...');
    const result = await Promise.reject(new Error('Boom!'));
    console.log('This will never be logged.');
  } catch (error) {
    // 錯誤被成功捕獲!
    console.error('Caught the error gracefully:', error.message);
    // 我們可以在這裡更新 UI,告訴使用者操作失敗
    // statusText.textContent = '操作失敗,請稍後再試。';
  }
}

safeOperation(); // 程式會優雅地處理錯誤,而不會崩潰。

鐵律:當你使用 await 來執行一個可能失敗的非同步操作時,永遠try...catch 將它包裹起來。


總結與後續

今天,我們為 async 函式注入了靈魂,徹底掌握了 await 關鍵字。

async/await 搭配 try...catch,這就是我們駕馭 Web Bluetooth API 乃至所有現代非同步 API 的終極武器組合。至此,所有關於非同步的理論準備工作已全部完成。我們已經磨好了劍,接下來就是要真正地出鞘了。

明天,我們將正式進入專案的第三階段。我們將學習 Web Bluetooth API 的基本規則與安全限制,並寫下第一行真正與瀏覽器藍牙功能互動的程式碼navigator.bluetooth.requestDevice()

那麼今天的內容就到這邊,感謝你能看到這裡,在這邊祝你早安、午安、晚安,我們明天見。


上一篇
Day 16:語法蜜糖 async:讓非同步程式碼更簡潔
下一篇
Day 18:Web Bluetooth API 導論:安全限制與瀏覽器支援
系列文
Web Bluetooth API 實戰:30 天打造通用 BLE 偵錯工具22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言