fetch() 简介

Matt Gaunt

fetch() 用于发出类似于 XMLHttpRequest (XHR) 的网络请求。两者的主要区别在于 Fetch API 使用 Promise,后者具有更简单的 API,可帮助您避免 XMLHttpRequest API 中的复杂回调。

浏览器支持

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

来源

如果您以前从未使用过Promise,请参阅 JavaScript Promise 简介

下面是一个先使用 XMLHttpRequest 再使用 fetch 实现的示例。我们想要请求网址,获取响应并将其解析为 JSON。

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。流是异步发生的。

响应元数据

前面的示例展示了 Response 对象的状态,以及如何将响应解析为 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);
});

响应类型

发出提取请求时,系统会为响应分配 response.type,值为“basic”“cors”或“opaque”。这些 types 显示资源的来源,您可以使用它们来确定如何处理响应对象。

当浏览器请求同一源的资源时,响应具有 basic 类型,并且会限制您可以从响应中查看的内容。

如果针对其他来源的资源发出请求,并且该来源返回 CORS 标头,则类型为 corscors 响应与 basic 响应类似,但它们会将您可以查看的标头限制为 Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma

opaque 响应来自不返回 CORS 标头的其他来源。如果响应不透明,我们将无法读取返回的数据或查看请求的状态,这意味着您无法检查请求是否成功。

您可以为提取请求定义模式,以便仅解析某些请求类型。您可以设置的模式如下所示:

  • same-origin 会成功请求同一来源的资产,并拒绝所有其他请求。
  • cors 允许请求同一来源以及返回相应 COR 标头的其他来源上的资源。
  • cors-with-forced-preflight 会在发出任何请求之前执行预处理检查
  • no-cors 旨在向没有 CORS 标头的其他源发出请求并导致 opaque 响应,但如上所述,目前在窗口全局范围内无法做到这一点。

如需定义模式,请在 fetch 请求中将 options 对象添加为第二个参数,并在该对象中定义模式:

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 请求

有时,Web 应用需要使用 POST 方法调用 API,并在请求正文中添加一些参数。为此,请在 fetch() 选项中设置 methodbody 参数:

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'
})