本文 AI 產出,尚未審核

fetch() 基本用法

簡介

在前端開發中,與後端或第三方 API 交換資料是最常見的需求之一。過去常用 XMLHttpRequest(簡稱 XHR)來發送 HTTP 請求,但語法冗長、錯誤處理不直觀,讓人望而卻步。ES6 之後,瀏覽器原生提供了 fetch(),它以 Promise 為基礎,讓非同步網路請求變得簡潔且易於閱讀。掌握 fetch() 的基本用法,不僅能提升開發效率,還能在 React、Vue、Angular 等框架中無縫整合,建立更具可維護性的資料流。

本篇文章針對 JavaScript 初學者至中級開發者,從概念說明、實作範例、常見陷阱到最佳實踐,完整呈現 fetch() 的核心功能與實務應用,幫助你快速上手並避免常見錯誤。

核心概念

1. fetch 的基本語法

fetch() 接收兩個參數:

fetch(url, options);
  • url:要請求的資源路徑(字串或 Request 物件)。
  • options(可選):設定 HTTP 方法、標頭、主體等資訊的物件。

fetch() 會回傳一個 Promise,解析成功時得到 Response 物件,失敗(例如網路斷線)則直接走到 catch

2. Response 物件與資料格式

Response 內建多種方法,用於把回傳的原始位元組(body)轉成開發者需要的型別:

方法 說明
response.text() 取得純文字
response.json() 解析 JSON(最常用)
response.blob() 取得二進位大檔案(如圖像)
response.arrayBuffer() 取得原始位元組陣列

這些方法本身也會回傳 Promise,因此常會出現 兩層 then 或使用 async/await 讓程式碼更平順。

3. 常用的 HTTP 方法與 options

fetch() 預設使用 GET 方法。如果要傳送資料(如 POST、PUT、DELETE),必須在 options 中明確指定:

{
  method: 'POST',          // HTTP 方法
  headers: {               // 請求標頭
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(data) // 主體資料(字串化)
}

⚠️ 注意body 必須是字串、BlobFormDataURLSearchParamsReadableStream,不能直接傳物件。


程式碼範例

下面提供 5 個實用範例,從最簡單的 GET 請求到進階的錯誤處理與自訂逾時機制,全部配合詳細註解。

範例 1:最簡單的 GET 請求

// 取得公開的 JSON 資料
fetch('https://api.github.com/users/octocat')
  .then(response => {
    // 確認 HTTP 狀態碼是否為 2xx
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.json(); // 解析成 JSON 物件
  })
  .then(data => {
    console.log('使用者資料:', data);
  })
  .catch(err => {
    // 網路錯誤或上面的 throw 會跑到這裡
    console.error('取得資料失敗:', err);
  });

重點response.oktrue 時代表狀態碼在 200~299 之間,建議在解析前先檢查。

範例 2:POST JSON 資料

const payload = {
  title: 'Fetch 教學',
  body: '這是一篇使用 fetch() 的教學文章。',
  userId: 1
};

fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST',                     // 使用 POST 方法
  headers: {
    'Content-Type': 'application/json' // 告訴伺服器傳送的是 JSON
  },
  body: JSON.stringify(payload)       // 把物件轉成字串
})
  .then(res => res.json())
  .then(result => {
    console.log('建立成功的回傳資料:', result);
  })
  .catch(console.error);

範例 3:使用 async/await 重寫 GET

async function getUser(username) {
  try {
    const response = await fetch(`https://api.github.com/users/${username}`);

    if (!response.ok) {
      // 直接拋出錯誤,讓 catch 捕捉
      throw new Error(`HTTP ${response.status}`);
    }

    const data = await response.json(); // 解析 JSON
    return data;                         // 回傳結果給呼叫端
  } catch (error) {
    console.error('取得使用者失敗:', error);
    // 依需求可回傳 null 或重新拋出錯誤
    return null;
  }
}

// 呼叫方式
getUser('octocat').then(user => console.log(user));

範例 4:同時發送多筆請求(Promise.all

const urls = [
  'https://jsonplaceholder.typicode.com/posts/1',
  'https://jsonplaceholder.typicode.com/posts/2',
  'https://jsonplaceholder.typicode.com/posts/3'
];

// 把每個 URL 包成 fetch Promise
const promises = urls.map(url => fetch(url).then(r => r.json()));

Promise.all(promises)
  .then(results => {
    console.log('三筆資料一次取得:', results);
  })
  .catch(err => {
    console.error('其中一筆請求失敗:', err);
  });

小技巧:若想保證所有請求都必須成功,使用 Promise.all;若想即使部分失敗也能取得其他結果,可改用 Promise.allSettled

範例 5:自訂逾時(Timeout)機制

fetch() 本身不支援逾時,需要自行包裝:

function fetchWithTimeout(url, options = {}, timeout = 5000) {
  // 建立一個會在 timeout 後 reject 的 Promise
  const timeoutPromise = new Promise((_, reject) => {
    const id = setTimeout(() => {
      clearTimeout(id);
      reject(new Error('Request timed out'));
    }, timeout);
  });

  // 用 Promise.race 同時執行 fetch 與 timeout
  return Promise.race([fetch(url, options), timeoutPromise]);
}

// 使用方式
fetchWithTimeout('https://jsonplaceholder.typicode.com/posts/1', {}, 3000)
  .then(res => res.json())
  .then(data => console.log('取得資料:', data))
  .catch(err => console.error('錯誤或逾時:', err));

常見陷阱與最佳實踐

陷阱 說明 解決方案
忘記檢查 response.ok 直接呼叫 response.json(),即使是 404/500 仍會解析,導致程式誤以為成功。 在解析前先判斷 response.ok,必要時拋出錯誤。
body 不是字串 fetchbody 必須是字串或 FormData,直接傳物件會拋 TypeError 使用 JSON.stringify()URLSearchParams 轉換。
跨域 (CORS) 錯誤 瀏覽器會阻擋未授權的跨域請求,錯誤訊息不容易看出。 確認伺服器端已設定 Access-Control-Allow-Origin,或使用 JSONP / 代理伺服器
忘記 awaitthen 忽略 Promise,導致程式在未取得資料前就往下執行。 使用 async/await 或正確鏈接 then
逾時未處理 fetch 在網路卡住時會一直等下去,使用者體驗差。 自行實作逾時(如上例),或使用第三方函式庫(axios、ky)。

最佳實踐

  1. 統一錯誤處理:封裝 fetchrequest() 函式,內部集中處理 response.ok、JSON 解析與例外拋出。
  2. 使用 async/await:讓非同步流程更直觀,尤其在多層 then 時。
  3. 設定合理的 Cache-ControlETag:減少不必要的請求,提升效能。
  4. 在開發環境使用 Mock Server:避免頻繁呼叫真實 API,提升測試效率。
  5. 避免在 URL 中直接拼接使用者輸入:使用 encodeURIComponent 防止注入攻擊。

實際應用場景

場景 需求 fetch 實作要點
取得部落格文章列表 前端頁面首次載入時向 CMS API 拉資料。 使用 GET,搭配 cache: 'no-store' 防止舊資料。
表單送出 使用者填寫表單後送出 JSON 到後端。 POST + Content-Type: application/json,使用 await 等待回應後顯示成功訊息。
上傳檔案 圖片或影片上傳至雲端儲存服務。 FormData 作為 bodyfetch(url, { method: 'POST', body: form }),同時監聽 onprogress(需要 XHR)或使用分段上傳。
即時搜尋建議 輸入框每次變更即向搜尋 API 取得建議。 加入 防抖(debounce)機制,並設定 逾時 防止舊請求回覆覆寫新結果。
多語系資源載入 根據使用者語系動態載入 JSON 翻譯檔。 依語系建立 URL,使用 Promise.all 同時載入多個檔案,失敗時回退至預設語系。

總結

fetch() 為現代前端提供了 簡潔、可讀且基於 Promise 的網路請求方式。掌握以下幾點,即可在日常開發中得心應手:

  1. 先檢查 response.ok,確保 HTTP 狀態正確。
  2. 根據需求選擇正確的 Response 方法.json().text().blob() 等)。
  3. 使用 async/await 讓非同步流程更自然。
  4. 自行實作逾時、錯誤統一處理,提升使用者體驗與除錯效率。
  5. 注意 CORS、body 格式與跨域安全,避免常見的陷阱。

只要熟練上述概念與實作範例,你就能在 React、Vue、Angular 或純 Vanilla JS 專案中,輕鬆完成資料取得、送出與錯誤處理,為前端與後端之間的溝通搭建穩固的橋樑。祝你寫程式快樂,玩轉 fetch()