Fetch() 簡介

Matt Gaunt

fetch() 可讓您發出類似 XMLHttpRequest (XHR) 的網路要求。主要差異在於 Fetch API 使用 Promise,其 API 較簡單,可協助您避免 XMLHttpRequest API 中複雜的回呼。

Browser Support

  • Chrome: 42.
  • Edge: 14.
  • Firefox: 39.
  • Safari: 10.1.

Source

如果您從未使用過 Promise,請參閱 JavaScript Promise 簡介。

基本 Fetch 要求

以下範例先使用 XMLHttpRequest 實作,再使用 fetch 實作。我們想要求網址、取得回應,並剖析為 JSON。

XMLHttpRequest

XMLHttpRequest 需要兩個事件監聽器來處理成功和錯誤情況,並呼叫 open() 和 send()。MDN 文件的範例。

function reqListener () {
  const data = JSON.parse(this.responseText);
  console.log(data);
}

function reqError (err) {
  console.log('Fetch Error :-S', err);
}

const oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.onerror = reqError;
oReq.open('get', './api/some.json', true);
oReq.send();

擷取

擷取要求如下所示:

fetch('./api/some.json')
  .then(response => {
    if (response.status !== 200) {
      console.log(`Looks like there was a problem. Status Code: ${response.status}`);

      return;
    }

    // Examine the text in the response
    response.json().then(function(data) {
      console.log(data);
    });
  })
  .catch(err => {
    console.log('Fetch Error :-S', err);
  });

fetch() 要求只需要呼叫一次,就能執行與 XHR 範例相同的工作。如要處理回應,請先確認回應狀態為 200,然後將回應剖析為 JSON。fetch() 要求的應答是 Stream 物件,也就是說,呼叫 json() 方法後,系統會傳回 Promise。串流會以非同步方式進行。

回應中繼資料

上一個範例顯示了回應物件的狀態,以及如何將回應剖析為 JSON。如要處理其他可能需要存取的中繼資料 (例如標頭),請按照下列步驟操作:

fetch('users.json').then(response => {
  console.log(response.headers.get('Content-Type'));
  console.log(response.headers.get('Date'));

  console.log(response.status);
  console.log(response.statusText);
  console.log(response.type);
  console.log(response.url);
});

回應類型

提出擷取要求時,回應會獲得「basic」、「cors」或「opaque」的 response.type。這些 types 會顯示資源來源,您可以使用這些資訊來判斷如何處理回應物件。

當瀏覽器要求相同來源的資源時,回應會具有 basic 類型,並限制您可以從回應中查看的內容。

如果要求另一個來源的資源,且該來源傳回 COR 標頭,則類型為 cors。cors 回應與 basic 回應類似,但會限制您可查看的標頭,僅限 Cache-Control、Content-Language、Content-Type、Expires、Last-Modified 和 Pragma。

opaque 回應來自其他來源,不會傳回 CORS 標頭。如果是不透明回應,我們就無法讀取傳回的資料或查看要求狀態,也就是說,您無法檢查要求是否成功。

您可以為擷取要求定義模式,只解析特定要求類型。可設定的模式如下:

  • same-origin 只會成功處理來自相同來源的素材資源要求,並拒絕所有其他要求。
  • cors 允許對相同來源和其他來源的素材資源提出要求,並傳回適當的 COR 標頭。
  • cors-with-forced-preflight 會在提出任何要求前執行預檢。
  • no-cors 的用途是向沒有 CORS 標頭的其他來源提出要求,並產生 opaque 回應,但如前所述,目前無法在視窗全域範圍內執行這項操作。

如要定義模式,請在 fetch 要求中新增選項物件做為第二個參數,並在該物件中定義模式:

fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
  .then(response => response.text())
  .then(text => {
    console.log('Request successful', text);
  })
  .catch(error => {
    log('Request failed', error)
  });

Promise 鏈結

Promise 的一大特色是能鏈結在一起。如果是 fetch(),這可讓您在擷取要求之間共用邏輯。

如果您使用的是 JSON API,則需要檢查狀態並剖析每個回應的 JSON。您可以簡化程式碼,方法是在傳回 Promise 的個別函式中定義狀態和 JSON 剖析,並使用擷取要求僅處理最終資料和錯誤案例。

function status (response) {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  } else {
    return Promise.reject(new Error(response.statusText))
  }
}

function json (response) {
  return response.json()
}

fetch('users.json')
  .then(status)
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  }).catch(error => {
    console.log('Request failed', error);
  });

這個範例定義了 status 函式,用於檢查 response.status,並傳回已解析的 Promise (Promise.resolve()) 或遭拒的 Promise (Promise.reject())。這是 fetch() 鏈中第一個呼叫的方法。

如果 Promise 解析成功,指令碼會呼叫 json() 方法,從 response.json() 呼叫傳回第二個 Promise,並建立包含剖析 JSON 的物件。如果剖析失敗,Promise 會遭到拒絕,並執行 catch 陳述式。

這種結構可讓您在所有擷取要求中分享邏輯,讓程式碼更容易維護、閱讀及測試。

POST 要求

有時網頁應用程式需要使用 POST 方法呼叫 API,並在要求主體中加入一些參數。如要這麼做,請在 fetch() 選項中設定 method 和 body 參數:

fetch(url, {
    method: 'post',
    headers: {
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
    },
    body: 'foo=bar&lorem=ipsum'
  })
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  })
  .catch(error => {
    console.log('Request failed', error);
  });

透過擷取要求傳送憑證

如要使用 Cookie 等憑證提出擷取要求,請將要求的 credentials 值設為 "include":

fetch(url, {
  credentials: 'include'
})