fetch() 简介

fetch() API 正在进入 window 对象,并正在查找替换 XHR

Matt Gaunt

XMLHttpRequest 很长

fetch() 允许您发出类似于 XMLHttpRequest (XHR) 的网络请求。主要区别在于,Fetch API 使用 promise,它可以实现更简洁、更简洁的 API,避免回调异常,并且必须记住 XMLHttpRequest 的复杂 API。

浏览器支持

  • 42
  • 14
  • 39
  • 10.1

来源

从 Chrome 40 开始,Fetch API 便已在 Service Worker 全局范围内使用,但在 Chrome 42 中,它将在窗口作用域内启用。您还可以立即使用一个非常实用的 GitHub 提供的 polyfill

如果您之前从未使用过 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);
    });

我们首先检查响应状态是否为 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 标头,则类型为 corscorsbasic 响应几乎完全相同,只不过 cors 响应将您可以查看的标头限制为 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 的一项出色功能是能够将它们链接在一起。对于提取,这允许您在提取请求之间共享逻辑。

如果您使用的是 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.resolve()Promise.reject() 的结果,后者会返回已解析或遭到拒绝的 Promise。这是 fetch() 链中调用的第一个方法;如果解析成功,则调用 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'
})

常见问题解答

如何取消 fetch() 请求?

目前无法取消提取,但 GitHub 上正在讨论这一点。H/T @jaffathecake

有 polyfill?

GitHub 提供了一个用于提取的 polyfill。H/T @Nexii 指出这一点。

为什么 Service Worker 支持“no-cors”,而窗口不支持?

这是出于安全方面的考虑,您可以点击此处了解详情。