0%

异步请求

XMLHttpRequest对象

XHR对象有一个readyState属性,表示当前处在请求/响应过程的哪个阶段

0:未初始化

1:已打开(open),已调用open()方法,未调用send()方法

2:已发送(send)已调用send()方法,尚未收到响应

3:接受中(Receiving),已经收到部分响应

4:完成(Complete),已经收到所有响应,可以使用

每次readyState从一个值变为另一个值,会触发readystatechange事件,可以借此机会检查readyState的值,一般来说,我们只关心readyState值是4,表示数据已经就绪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var form = document.getElementById("myForm");
let xhr=new XMLHttpRequest();
var formData = new FormData(form);
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
if((xhr.status>=200&&xhr.status<300)||xhr.status===304){
alert(xhr.responseText)
}else{
alert("Request was unsuccessful:"+xhr.status)
}


}
xhr.open("post","http://127.0.0.1:3007/api/login",true)
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')//设置发送请求的头部
xhr.timeout=1000;//设置超时
xhr.ontimeout=function(){
alert("Request didn not return in a second")
}
xhr.send(serialize(form))
//使用FormData
//xhr.send(new FormData(form))

}

凭据请求:

默认情况下,跨域请求不提供凭据(cookie,HTTP认证和客户端SSL证书),可以通过withCredentials属性设置为true来表明请求会发送凭据,如果服务器允许待凭据的请求,那么可以在响应中包含如下HTTP头部:Access-Control-Allow-Credentials:true

如果发送了凭据请求而服务器返回的响应中没有这个头部,则浏览器不会把响应交给JavaScript,服务器也可以在预检请求的响应中发送这个HTTP头部,以表明这个源允许发送凭据请求

Fetch API

fetch()只有一个必须的参数input,这个参数是要获取的资源的URL,这个方法返回一个期约;

当你的服务端返回的数据是 JSON 格式时,你肯定希望 fetch 返回给你的是一个普通 JavaScript 对象,然而你拿到的是一个 Response 对象,而真正的请求结果 —— 即 response.body —— 则是一个 ReadableStream 。

此外, Response 还限制了响应内容的重复读取和转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var button=document.querySelector('[type=button]')
button.onclick=function(){
var form = document.getElementById("myForm");
let paramsHeaders=new Headers({
'Content-Type':'application/x-www-form-urlencoded'
});
fetch('http://127.0.0.1:3007/api/login',{// 服务端返回 {"name": "test", "age": 1} 字符串
method:'POST',
body:serialize(form),
headers:paramsHeaders
}).then((res)=>{
return res.json()// 将 response.body 通过 JSON.parse 转换为 JS 对象并且记得return给下一个.then使用,或者使用res.text()
},(err)=>{
console.log(err)
})

常见Fetch请求模式

发送JSON数据

1
2
3
4
5
6
7
8
9
let payload=JSON.stringfy({foo:'bar'});
let jsonHeaders=new Headers({
'Content-Type':'application/json'
})
fetch('/send-me-json',{
method:'POST',
body:payload,
headers:jsonHeaders
})

请求体中发送参数

1
2
3
4
5
6
7
8
9
let payload='foo=bar&baz=qux';
let paramheaders=newHeaders({
'Content-Type':'application/x-www-form-urlencoded'
})
fetch('/send-me-params',{
method:'POST',
body:payload,
headers:paramHeaders
})

发送文件:

fetch()序列化并发送文件字段中的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
let imageFormData=new FormData();
let imageInput=document.querySelector("input[type='file']");
//上传多个文件
/*imageInput=document.querySelctor("input[type='file'][multiple]");
for(let i=0;i<imaeInput.files.length;++i){
imageFormData.append('image',imageInput.files[i])
}*/
imageFormData.append('image',imageInput.files[0]);
fetch('/img-upload',{
method:'POST',
body:imageFormData
})

中断请求:

通过AbortController/AbortSignal对中断请求,调用AbortController.abort()会中断所有网络的传输

1
2
3
4
5
6
let abortController=new AbortController();
fetch('wikipedia.zip',{signal:abortController.signal})
.catch(()=>{console.log('aborted');
//10ms中断请求
setTimeout(()=>abortController.abort(),10)
})

Request对象

通过构造函数初始化Request对象,接收两个参数,第一个参数是一个input参数,一般是URL,第二个参数是一个init对象,没有在init对象中涉及的值会使用默认值

创建Request对象副本

  • 创建Request构造函数
  • 使用clone()方法

使用构造函数:

1
2
3
4
5
6
let r1=new Request('http://foo.com');
let r2=new Request(r1,{method:'POST'});
console.log(r1.method);//GET
console.log(r2.method);//POST
//这种克隆方式不能总得到一模一样的副本

使用clone

请求体被读取后再克隆会导致抛出TypeError

1
2
3
4
5
6
7
let r=new Request('http://foo.com');
r.clone();
new Request(r);//没有错
r.text();//设置bodyUsed为true
r.clone();//TypeError


在fetch()中使用Request对象

如果有第二个参数init对象,也会覆盖传入请求对象的值

1
2
3
4
5
6
7
8
9
10
let r=new Request('http://127.0.0.1:3007/api/login');
fetch(r,{
method:'POST',
body:serialize(form),
headers:paramsHeaders
}).then((res)=>{
return res.json()
},(err)=>{
console.log(err)
})

要基于包含请求体的相同Request对象多次调用fetch(),必须在第一次发送fetch()请求前调用clone()

1
2
3
4
let r=new Request(...);
fetch(r.clone());
fetch(r.clone());
fetch(r);

Response对象

产生Response对象的主要方式是调用fetch(),它返回一个最后会解决Response对象的期约

响应状态信息

1

其它问题

  1. fetch 不支持同步请求

大家都知道同步请求阻塞页面交互,但事实上仍有不少项目在使用同步请求,可能是历史架构等等原因。如果你切换了 fetch 则无法实现这一点。

  1. fetch 不支持取消一个请求

使用 XMLHttpRequest 你可以用 xhr.abort() 方法取消一个请求(虽然这个方法也不是那么靠谱,同时是否真的「取消」还依赖于服务端的实现),但是使用 fetch 就无能为力了,至少目前是这样的。

  1. fetch 无法查看请求的进度

使用 XMLHttpRequest 你可以通过 xhr.onprogress 回调来动态更新请求的进度,而这一点目前 fetch 还没有原生支持。