Fetch 与 XMLHttpRequest
在浏览器中,我们常常会遇到需要向服务器发送请求获取信息的场景,即无需全屏刷新就可以获取对应信息。以上情景通常被称为 Ajax 请求或 SPA(单页应用程序)。
从 1999 年到 2015 年,XMLHttpRequest 是实现要求的唯一方式 。虽然 Ajax 全名是“异步的 JS + XML”,但 XMLHttpRequest 允许细粒度的控制,它可以处理除XML 以外格式的响应,比如文本、二进制、JSON 和 HTML。
从 2015 年开始,浏览器实现了 Fetch API,Fetch API 提供了一个获取资源的接口,对于任何使用过 XMLHttpRequest 的人都能轻松上手,它提供了一种更精简、更容易、更一致的替代方案。
Fetch使用
fetch(url)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error))
fetch() 调用返回一个 Promise,可以通过对 Promise 提供有关结果信息的 Response 对象进行解析获取想要的内容。
const response = await fetch(url, {
method: 'GET'
})
fetch() 接受两个参数:
- resource 资源路径:字符串或 URL 对象
- options 可选选项参数
options 对象可以在 Node 或客户端代码中设置属性,可设置部分属性如下:
属性 | 值 |
---|---|
method | GET/POST/PUT/PATCH/DELETE/HEAD |
headers | Headers 对象 |
body | 字符串、FormData、Blob 等 |
mode | same-origin/no-cors/cors/navigate/websocket |
credentials | omit/same-origin/include |
redirect | follow/error/manual |
referrer | 引用 URL |
priority | high/low/auto |
integrity | 子资源完整性哈希URL |
cache | default/no-store/reload/no-cache/force-cache/only-if-cached |
signal | 用于取消请求的 AbortSignal 对象 |
其中关于我们常用的请求头headers设置,可以使用 Headers 对象相关操作对请求和响应中的 HTTP header进行检查。
// 初始化 headers
const headers = new Headers({
'Content-Type': 'text/plain'
})
// 添加
headers.append('Authorization', 'Bear abc123')
// 添加/更改
headers.set('Content-Type', 'application/json')
// 获取
const type = headers.get('Content-Type')
// 判断
if (headers.has('Authorization')) {
// 删除
headers.delete('Authorization')
}
// 循环
headers.forEach((value, name) => {
console.log(`${name}: ${value}`)
})
// 在 fetch() 中使用
const response = await fetch(url, {
method: 'GET',
headers
})
// response.headers 返回一个 Headers 对象
response.headers.forEach((value, name) => {
console.log(`${name}: ${value}`)
})
fetch响应
当接收到一个 HTTP 错误状态码时,从 fetch() 返回的 Promise 不会被标记为 reject, 即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve 。
fetch() Promise 只在以下情况下才会返回 reject:
- 发送请求无效,比如资源路径有误: fetch('httttps://!invalid\URL/');
- fetch() 请求中止
- 请求出现网络错误,比如连接失败
那么既然大多数场景下我们会收到 resolve, 那么该如何判断接口返回是否真正成功呢--就得依赖 fetch 返回的 response 对象进行判断了。
成功的fetch()调用将返回一个 Response 对象,对象中包含相关状态及返回数据的信息。Response 对象主要属性如下:
属性 | 说明 |
---|---|
headers | 响应 Headers 对象 |
ok | 布尔值,若响应成功则为 true |
redirected | 是否来自重定向 |
status | HTTP 状态码,如 200 表示成功 |
statusText | HTTP 状态文本,比如OK对应 200 |
type | 响应类型:basic、cors等 |
url | URL |
body | body 内容 |
bodyUsed | body 是否读取 |
Response 对象常用的方法如下所示,他们都返回一个 Promise。
clone() | 克隆响应 |
---|---|
text() | 返回被解析为字符串的Promise |
json() | 返回被解析为 JSON 的Promise |
arrayBuffer() | 返回被解析为 ArrayBuffer 的Promise |
blob() | 返回被解析为 Blob 的Promise |
formData() | 返回被解析为 FormData 的Promise |
// response 示例
const response = await fetch(url)
// 响应返回 JSON
if (
response.ok &&
response.headers.get('Content-Type') === 'application/json'
) {
// 解析 JSON
const result = await response.json()
console.log(result, 'result')
}
如何中止fetch请求
在实际应用中有以下情况需要中止请求:
- 避免接口请求时间过长仍未得到服务器响应的情况,我们应该设置超时时间,当请求时间大于超时时间未得到响应时及时中止请求
- 避免未得到响应的无效请求占用资源
那么fetch如何中止请求呢?
// 创建 AbortController,1分钟后超时中止
const controller = new AbortController()
const signal = controller.signal
const timeout = setTimeout(() => controller.abort(), 60000)
try {
const response = await fetch(url, { signal })
clearTimeout(timeout)
console.log(response.ok)
} catch (err) {
// timeout or network error
console.log(err)
}
fetch中使用 AbortController 对象,它将 signal 属性作为options传递给 fetch() 参数, 如果接口在设定时间内没有返回响应,那么就会触发取消请求。除此之外,也可根据使用场景通过 controller.abort()手动取消对应请求。