fetch() 简介

Matt Gaunt

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

浏览器支持

  • 42
  • 14
  • 39
  • 10.1

来源

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

基本提取请求

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

XMLHttpRequest

XMLHttpRequest 需要两个监听器来处理成功和错误情况,以及对 open()send() 的调用。MDN 文档中的示例

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

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

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

提取

我们的提取请求如下所示:

fetch('./api/some.json')
  .then(
  function(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(function(err) {
    console.log('Fetch Error :-S', err);
  });

fetch() 请求只需调用一次即可执行与 XHR 示例相同的工作。为了处理响应,我们首先检查响应状态是否为 200,然后将响应解析为 JSON。对 fetch() 请求的响应是一个 Stream 对象,这意味着在调用 json() 方法后,系统会返回一个 Promise。流会异步发生。

响应元数据

上面的示例展示了 Response 对象的状态,以及如何将响应解析为 JSON。下面是处理您可能想要访问的其他元数据(例如标头)的方法:

fetch('users.json').then(function(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 类型,该类型限制您可以从响应中查看的内容。

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

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(function(response) {
    return response.text();
    })
    .then(function(text) {
    console.log('Request successful', text);
    })
    .catch(function(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(function(data) {
    console.log('Request succeeded with JSON response', data);
    }).catch(function(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(function (data) {
    console.log('Request succeeded with JSON response', data);
    })
    .catch(function (error) {
    console.log('Request failed', error);
    });

使用提取请求发送凭据

如需使用 Cookie 等凭据发出提取请求,请将请求的 credentials 值设置为 "include"

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