Fetch API: 异步请求新解决方案
浏览器通过XMLHttpRequest(XHR)来实现异步请求,但是这种方式存在如下问题:
- 职责不分离: 输入、输出、结果处理均通过XHR对象来完成
- 事件机制: 结果的处理通过事件模型来完成,会存在回调嵌套问题(callback hell),这与流行的Promise不搭配
基于以上原因,出现了fetch api,也经常跟service worker一起搭配使用。fetch api下存在Headers、Request、Response三个类,直接与HTTP的相关定义对应起来,异步操作变得更加灵活、强大。
Headers
通过Headers,可以方便地对request、response的Headers信息进行增、删、改、查的操作:
- 增: Headers.prototype.append
- 删: Headers.prototype.delete
- 该: Headers.prototype.set
- 查: Headers.prototype.get、Headers.prototype.getAll、Headers.prototype.entries、Headers.prototype.keys、Headers.prototype.has
还有就是查询的某些接口返回的是iterator,这样就可以使用for…of进行遍历:
const header = new Headers;
header.append('content-type', 'application');
header.append('connection', 'keep-alive');
for (let item of header.entries()) {
console.log(item)
}
// ["content-type", "application"]
// ["connection", "keep-alive"]
// 上述代码可以直接写成
for (let item of header) {
console.log(item)
}
其中因为安全因素, Headers有些属性是不可写的,header对象中存在一个不对外的属性guard
,规定了不同情况(none
/request
/request-no-cors
/response
/immutable
)下Headers的行为。
Body
无论Request还是Response,里面均存在body
属性,该属性存储请求的内容,由于javascript里面数据类型的复杂性: ArrayBuffer
、Blob
、string
、URLSearchParam
、FormData
,所以fetch提供了mixin类Body
,用于处理不同类型的数据。
Body除了具有相关数据类型的处理方法外,还有一个字段bodyUsed
,用于标示该body
是否已经被读取。因为body
是stream,只能被读取一次。
那么如何要多次读取body
里面的内容,就需要首先进行clone
。
Request
Request定义了HTTP请求获取资源的request格式,接受两个参数: url
、options
,实际中往往不需要手动new一个request对象,通过其他操作会返回一个request对象:
const req = new Request('http://example.com/api/data', {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: 'name=test&age=12',
mode: 'cors',
credentials: 'include',
});
console.log('request body is readed ? : ',req.bodyUsed);
req.json().then((json) => {
console.log(json);
console.log('request body is readed ? : ',req.bodyUsed);
}).catch((err) => {
console.log(err)
});
// request body is readed ? : false
// demo:13 Object {name: "test", count: 123}
// request body is readed ? : true
相对于XHR方式,Request对象可以很方便、明确地显示是否允许跨域(mode)、是否需要携带cookie(credentials)等。
Response
Response对应http的相应,里面包含了服务相应的相关信息,除了在service worker中,是不需要显示创建。
fetch('/api/data').then((res) => {
console.log('res headers: ', res);
return res.json();
}).then((json) => {
console.log('json2 is: ', json);
}).catch((err) => {
console.log('the error is: ', err);
})
Response里面存在如下属性来描述HTTP相应的结果:
- status/statusText/ok: 藐视该请求的状态,其中当
status=2**
时,ok === true
- type: 其中肯呢过为
basic
、cors
、error
、opaque
,指明该HTTP请求是否跨域、错误等信息 - body: 存储响应的内容
结语
在github上已经出现了fetch的polyfill,并且非常流行,厌倦XHR写法的同学可以尝试该方法来解决异步请求问题。