Fetch API 简单指南
什么是 Fetch API
fetch api 是一个基于Promise api设计的 xmlHttpRequest 的升级替代品,用于通过javascript发起异步请求。
目前各个主流浏览器的主要版本都内置了Fetch Api;
浏览器支持列表:

既然说fetch是xhr的替代品,那就不得不来说一下他相对xhr来说有哪些优点:
-
fetch 较于 xhr 来讲,最大的优点在于他是基于Promise的,这样一来你可以方便的进行链式调用,也可以完全利用Promise丰富的api,实现一些复杂的需求。
-
fetch 遵循模块化设计原则和OOB原则。fetch将一个完整的请求需要的功能按照Http请求的模块划分分散到几个主要的接口上:Request, Response, Header,AbortController, 这样一来,使用起来各部分逻辑关注分离,使得代码更加清晰易懂。而xhr则是将所有的集中在一个接口上。
-
fetch采用异步数据流处理响应数据,这样一来,数据可以做到按需加载。同xhr必须将数据放入缓存才能使用相比,能大大提高应用的性能。
-
fetch 同样可以被中止,并且可以被优雅的批量终止。
当然,fetch相比于xhr,也有一个缺点,那就是他不支持进度状态,因此,在一些文件上传下载的业务中,可能不是那么的友好。
开始使用:hello fetch
fetch api本身只是一个函数,因此的他基础用法很简单:
fetch('http://some.url.com/api').then(response => {
...
})
fetch函数的第一个参数可以是一个资源Url,此时,如果没有第二个参数,将会发起一个简单的get请求。
返回值是一个提供一个Response实例的Promise.
值得一提的是,只要服务端成功响应了本次请求,无论状态码是多少,Promise的状态都是fullfilled状态,只有当遇到网络错误,或者URL无效或者其他原因服务器根本没有响应时,promise的状态才会是rejected.
关于Response
有必要说一下Response对象常用的的Api:
Response对象时对服务端响应的一个完全封装,response包含一下属性或者方法:
-
ok属性: 用来标识请求是否成功响应(http状态码在200-299之间)
-
status属性: 响应的状态码
-
statusText属性:响应状态信息说明,例如status为200,statusText 为 OK.
-
type属性: 标识响应的类型, 常用的值有 cors(跨域请求),basic(同源请求), error(网络错误导致的失败的请求)
-
url属性: 响应对应的URL
-
Response.useFinalURL属性: 包含了一个布尔值,来标示这是否是该 Response 的最终 URL。
-
Response.body属性:一个简单的 getter,用于暴露一个 ReadableStream 类型的 body 内容。
-
Response.bodyUsed属性:包含了一个布尔值 (en-US)来标示该 Response 是否读取过
Body。 -
Response.arrayBuffer():读取 Response 对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为 ArrayBuffer 格式的 Promise 对象。
-
Response.blob():读取 Response 对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为 Blob 格式的 Promise 对象。
-
Body.formData(): 读取Response 对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为 FormData 格式的 Promise 对象
-
Body.json(): 读取 Response 对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为
JSON格式的 Promise 对象。 -
Body.text(): 读取 Response 对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为 USVString 格式的 Promise 对象。
具体使用细节后面会提到。
复杂的请求
发起一个复杂的请求有两种方式:
fetch options
fetch第一个参数为url字符串的时候,可以传入一个配置对象最为请求的各种选项参数,例如
fetch("http://my.service.com/api/path",{ method:'POST', headders:new Headers({ Authorization: '*****' }) })
.then(resp => resp.json(),e => console.error(e))
.then(data => {
console.log(data);
})
options的可选属性列表如下:
-
method: 请求使用的方法,如GET、``POST。 -
headers: 请求的头信息,形式为 Headers 的对象或包含 ByteString 值的对象字面量。 -
body: 请求的 body 信息:可能是一个 Blob、BufferSource (en-US)、FormData、URLSearchParams 或者 USVString 对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。 -
mode: 请求的模式,如cors、no-cors 或者same-origin。 -
credentials: 请求的 credentials,如omit、``same-origin 或者include。为了在当前域名内自动发送 cookie , 必须提供这个选项, 从 Chrome 50 开始, 这个属性也可以接受 FederatedCredential (en-US) 实例或是一个 PasswordCredential (en-US) 实例。 -
cache: 请求的 cache 模式:default、no-store、reload、no-cache、force-cache或者only-if-cached。 -
redirect: 可用的 redirect 模式:follow(自动重定向),error(如果产生重定向将自动终止并且抛出一个错误), 或者manual(手动处理重定向). 在Chrome中默认使用follow(Chrome 47之前的默认值是manual)。 -
referrer: 一个 USVString 可以是no-referrer、``client或一个 URL。默认是client。 -
referrerPolicy: 指定了HTTP头部referer字段的值。可能为以下值之一:no-referrer、no-referrer-when-downgrade、origin、origin-when-cross-origin、unsafe-url 。 -
signal: 终止信号
比较有意思的是signal属性,详细的会在终止fetch部分说明。
Request对象
fetch也可以直接接受一个Request对象来发起一个复杂的请求,例如上面的例子也可以这样写:
let request = new Request("http://my.service.com/api/path",{ method:'POST', headders:new Headers({ Authorization: '*****' });
fetch(request).then(resp => {
if(resp.ok) {
return resp.json();
} else {
return Promise.reject("error");
}
})
Request 构造函数的第二个参数可选属性实际上和上面fetch第二个参数的可选参数一致。
中止fetch
上面提到了signal属性可以用于中止一个fetch请求,接下来详细说一下。
Fetch终止需要借助另一个接口AbortController, AbortController的使用方法很简单:
let abortController = new AbortController();
AbortController的实例只包含一下两个属性或方法:
-
abort()方法: 用于中止一个尚未完成的fetch请求操作。
-
signal: 返回一个AbortSignal对象实例,你可以将signal作为fetch第二个参数的属性或者作为Request第二个参数的属性传递给fetch来控制fetch的状态。
abort调用时,会通知所有使用了这个AbortController实例的signal的fetch请求中止操作。
你可以通过signal.addEventListener('abort',callback) 或者 signal.onabort = callback 的方式来监听abortController的中止事件,当你调用abortController实例的abort()方法时,你的callback函数会被出发,并且获得一个abortEvent;同时如果fetch未完成,那么fetch返回的Promise的状态会变为rejected, reject回调会获得一个名称为AbortError的异常。
例如:
const ac = new AbortController();
const { signal } = ac;
ac.addEventListener('abort',e => console.log('aborted',e))
const req = new Request('http://www/baidu.com',{ signal })
fetch(req).then(resp => resp.text(),e => console.log(e.name))
setTimeout(() => { ac.abort() },1)
//aborted
// Evnet { type:"abort" ... }
// AbortError
几个栗子
1. 文件上传
const file = document.querySelector("input#file").file[0];
const data = new FormData();
data.append('file', file);
const req = new Request('upload/uri', { method:"POST", body: data })
.then(resp => {
...
})
2. JSON数据提交
const headers = { "Content-Type":"application/json" }
const = {
name:'jimi',
age: 18
}
const req = new Request('upload/uri', { method:"POST", headers, body: JSON.stringify(data) })
fetch.then(resp => {
return resp.json()
}).then(data => {
...
})
3. 文件下载
const req = new Request('https://images2015.cnblogs.com/blog/408483/201702/408483-20170221215434991-1565736785.png');
fetch(req).then(resp => {
return resp.blob()
}).then(img => {
let imgSrc = URL.createObjectURL(img);
let imgNode = document.createElement("img");
imgNode.setAttribute('src', imgSrc);
document.body.appendChild(imgNode);
})
特别注意
需要特别注意的一点,fetch api返回的response采用流式数据接口,所以一旦数据被读取,你将无法再次读去到数据:例如下面的代码中第二次获取json数据就会报错:
const headers = { "Content-Type":"application/json" }
const = {
name:'jimi',
age: 18
}
const req = new Request('upload/uri', { method:"POST", headers, body: JSON.stringify(data) })
const response = await fetch(req);
const json = await response.json();
const errorJson = await response.json() // Failed to execute 'json' on 'Response': body stream already read
如果确实需要二次读取response的数据流,可以使用response.clone()克隆出一个新的response,数据流读取使用基于被克隆的response对象操作,后续需要再次读取数据,只需要再次克隆一根原来的未被读取的response:
const headers = { "Content-Type":"application/json" }
const = {
name:'jimi',
age: 18
}
const req = new Request('upload/uri', { method:"POST", headers, body: JSON.stringify(data) })
const response = await fetch(req);
const json = await response.clone().json();
const errorJson = await response.clone().json() // 可以正常读取数据

Fetch API 是一个基于Promise的XMLHttpRequest替代品,广泛应用于现代浏览器。本文介绍了Fetch的基本使用,包括发起简单和复杂请求,使用Response对象,以及如何中止请求。通过示例展示了文件上传、JSON数据提交和文件下载。特别指出,Fetch不支持进度状态,但可以通过AbortController实现请求中止。
2689

被折叠的 条评论
为什么被折叠?



