Fetch API 简单指南

Fetch API 简单指南

什么是 Fetch API

fetch api 是一个基于Promise api设计的 xmlHttpRequest 的升级替代品,用于通过javascript发起异步请求。

目前各个主流浏览器的主要版本都内置了Fetch Api;

浏览器支持列表:

Fetch API 简单指南_第1张图片

既然说fetch是xhr的替代品,那就不得不来说一下他相对xhr来说有哪些优点:

  1. fetch 较于 xhr 来讲,最大的优点在于他是基于Promise的,这样一来你可以方便的进行链式调用,也可以完全利用Promise丰富的api,实现一些复杂的需求。

  2. fetch 遵循模块化设计原则和OOB原则。fetch将一个完整的请求需要的功能按照Http请求的模块划分分散到几个主要的接口上:Request, Response, Header,AbortController, 这样一来,使用起来各部分逻辑关注分离,使得代码更加清晰易懂。而xhr则是将所有的集中在一个接口上。

  3. fetch采用异步数据流处理响应数据,这样一来,数据可以做到按需加载。同xhr必须将数据放入缓存才能使用相比,能大大提高应用的性能。

  4. 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 模式: defaultno-storereloadno-cacheforce-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的实例只包含一下两个属性或方法:

  1. abort()方法: 用于中止一个尚未完成的fetch请求操作。

  2. 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() // 可以正常读取数据

你可能感兴趣的:(JavaScript,Web,Api,前端,javascript,fetch,AbortControler)