【译】Fetch API

2,396 阅读6分钟

fetch是一种使用 promise 为构建块的现代异步网络请求方法

简介

1998年发布的IE5,让我们能够使用 XMLHttpRequest (XHR) 对象在浏览器中进行异步网络请求。 在此后的几年,GMail 和其他丰富的应用程序大量使用它,并使该方法如此受迎,以至于它必须有一个名称:AJAX。 直接使用 XMLHttpRequest 一直很痛苦,所以它经常被一些库抽象集成,比较有代表性的比如说 jQuery 使用 XHR 封装了自己的辅助函数,比如:

  • jQuery.ajax()
  • jQuery.get()
  • jQuery.post()
  • ...

这些方法对更方便的使用 XHR 发送异步请求有很大的影响,特别是它们也确保了在旧浏览器上也能够正常工作。Fetch,是当今进行异步网络请求的新标准,并且使用了 Promise 作为构建块,除了 IE 之外,Fetch 在各主要浏览器中都能够得到很好的支持。

browser-support
另外 Github 提供了fetch方法的 Polyfill,让我们可以在任何浏览器里面使用 fetch。

使用Fetch

使用 Fetch 方法发起 GET 请求是非常简单的:

fetch('/file.json')

当你已经开始使用它:fetch 将会向相同的域名发起一个 HTTP 请求以获得域名下 file.json 资源。 如您看见的那样,fetch 方法可在全局 window 作用域下获得,现在我们让它变得更有意义,它在实际情况下的使用下可能是这样的:

fetch('./file.json')
  .then(response => response.json())
  .then(data => console.log(data))

调用 fetch() 会返回一个 promise 对象,我们可以等待这个 promise 对象 resolve 之后将返回的结果传递给 promise 对象的 then 方法。then 里面的回调函数接收到的 fetch Promise 返回的对象,即 Response 对象。在下面会详细介绍这个 Response 对象。

捕获错误

因为 fetch() 方法会返回一个 Promise 对象,我们可以使用 promise 对象的 catch 方法拦截在请求执行过程中发生的任何错误,以及在 then 的回调函数中的处理结果:

fetch('./file.json')
.then(response => {
  //...
})
.catch(err => console.error(err))

另一个捕获请求发生错误的方法是在第一个 then 里面对它进行处理

fetch('./file.json')
.then(response => {
  if (!response.ok) { throw Error(response.statusText) }
  return response
})
.then(response => {
  //...
})

响应对象

调用 fetch() 返回的响应对象包含有关网络请求和响应的所有信息。

元数据

headers

通过访问响应对象上的 headers 属性使您能够查看请求返回的 HTTP 头:

fetch('./file.json').then(response => {
  console.log(response.headers.get('Content-Type'))
  console.log(response.headers.get('Date'))
})

HTTP Headers


status

此属性是表示 HTTP 响应状态的整数 ( 响应头部的状态码 )。

  • 101,204,205 或 304 是响应体没有数据的状态
  • 200 到299,包括两边的值,是一个OK状态(成功)
  • 301,302,303,307 或 308 是重定向
fetch('./file.json').then(response => console.log(response.status))
statusText

statusText 是表示响应状态消息的属性。如果请求成功,则状态为OK。

fetch('./file.json').then(response => console.log(response.statusText))
url

url 是表示我们请求资源的完整 URL 的属性

fetch('./file.json').then(response => console.log(response.url))

响应主体内容

可以通过下面这些方法得到响应主体的内容:

  • text()将主体内容作为字符串返回
  • json()将主体内容经过 JSON.parse 转换后返回
  • blob()将主体内容作为 Blob 对象返回
  • formData()将主体内容作为 FormData 对象返回
  • arrayBuffer()将主体内容作为 arrayBuffer 对象返回

所有这些方法都会返回一个 Promise,比如说:

fetch('./file.json')
  .then(response => response.text())
  .then(body => console.log(body))
fetch('./file.json')
  .then(response => response.json().catch(()=>({})))//响应状态码为 200 但没有响应主体内容的时候:undefined,使用json()方法会报错,因此需要catch到这个错误
  .then(body => console.log(body))

JSON.parse只能解析符合 JSON 语法 的字符串,如果要解析的内容不是字符串的时候,会调用 Object.prototype.toString() 方法,然后再进行解析


text json


可以使用 ES2017 的 async 方法对上面的方法进行改写:

;(async () => {
  const response = await fetch('./file.json')
  const data = await response.json()
  console.log(data)
})()

请求对象

请求对象表示对资源的请求,通常可以由 new Request() 方法生成。 比如说:

const req = new Request('/api/todos')

请求对象提供了一些只读属性来检查请求的详细信息,包括:

  • method:请求的方法,包括:GET、POST、PUT、HEAD、DELETE 等等
  • url:请求的 URL
  • headers:请求的关联请求头的对象
  • referrer:请求的引用者
  • cache:请求的缓存模式(比如说:default、reload、no-cache) 还公开了一些处理请求主体的方法:json()text()formData() 完整的 API 可以在 Request 中找到。

请求头部

我们经常需要能够设置 HTTP 请求头部,fetch 使我们能够用 Headers 对象执行此操作:

const headers = new Headers()
headers.append('Content-Type', 'application/json')

或者写成👇这样:

const headers = new Headers({
  'Content-Type': 'application/json'
})

要使请求带上请求头,我们将 Request 对象传给 fetch() 而不是只传 URL。 相比于:

fetch('./file.json')

我们这样做:

const request = new Request('./file.json', {
  headers: new Headers({
    'Content-Type': 'application/json'
  })
})
fetch(request)

Header 对象不仅可以设置键值对,我们也可以查询其中的值:

headers.has('Content-Type')
headers.get('Content-Type')

并且,我们也可以删除之前设置的请求头:

headers.delete('X-My-Custom-Header')

POST 请求

Fetch 也允许在我们的请求中使用其他的 HTTP 请求方法,比如说:POST, PUT, DELETE 或者 OPTIONS。 在请求对象的 method 属性中指定方法,并在请求头和请求体中传递其余参数: POST 请求的例子:

const options = {
  method: 'post',
  headers: {
    'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
  },
  body: 'name=Flavio&test=1'
}

fetch(url, options).catch(err => {
  console.error('Request failed', err)
})

怎么取消 fetch 请求

fetch 被推出的这几年,一旦使用 fetch 发起一个请求就没有办法终止这个请求,现在,我们可以通过引入通用 API: AbortController 和 AbortSignal 来通知中止事件。 您可以将 signal 作为 fetch 的参数传递给 fetch

const controller = new AbortController()
const signal = controller.signal

fetch('./file.json', { signal })

比如,您可以设置一个定时器当 fetch 请求执行 5s 之后终止请求:

setTimeout(() => controller.abort(), 5 * 1000)

即使这个请求已经返回了,调用 abort() 也不会导致任何错误, 当中止信号发出后,fetch 将会由被叫做 AbortErrorDOMException对象(DOMException是W3C DOM核心对象。DOMException接口表示一个处理的错误,当一个操作不可能执行的时候,会抛出一个异常。例如试图创建一个无效的DOM, 或通过一个不存在的节点作为参数节点操作方法)reject 返回的 promise:

fetch('./file.json', { signal })
  .then(response => response.text())
  .then(text => console.log(text))
  .catch(err => {
    if (err.name === 'AbortError') {
      console.error('Fetch aborted')
    } else {
      console.error('Another error', err)
    }
  })

阅读更多

网络请求非常难,对吗?您可能发现了当您需要一些基于 Fetch 构建的附加功能时, Axios 这个 JavaScript 库更加符合您的需求,相比于 Fetch 它有这些优点:

  • 支持IE8及更高版本的浏览器(Fetch 需要 Polyfill)
  • 有办法可以终止请求
  • 内置 CSRF 保护
  • 执行自动JSON数据转换 ...

那现在就去学习吧!

原文地址
扩展阅读:XMLHttpRequest对象