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