Fetch超时设置与终止请求abort

fetch请求和ajax请求 fetch与ajax(XMLHttpRequest)相比

ES6中新增了一种HTTP数据请求的方式,就是fetch,它和XMLHttpRequest有许多相似的功能,但是相比XMLHttpRequest,fetch被设计成更具可扩展性和高效性。江湖上一直流传着 “传统ajax已死,fetch永生”的说法,下面详细说下二者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
response.arrayBuffer()
读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为ArrayBuffer格式的promise对象

response.blob()
读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为Blob格式的promise对象

response.formData()
读取Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为FormData格式的promise对象

response.json()
读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为JSON格式的promise对象

response.text()
读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为USVString格式的promise对象
  • 语法简洁,更加语义化
  • 基于标准 Promise 实现,支持 async/await
  • 同构方便,使用 isomorphic-fetch
  • fetch 一直没有传统的 XHR 所拥有的 abort() ,setTimeout超时回调 和 onprogress 功能
    1
    2
    3
    4
    5
    6
    7
    8
    9
    fetch(url,{
    method:'POST', // method:'PUT', method:'DELETE',
    headers:{
    'Content-type':'application/json'// 设置请求头数据类型
    },
    body:data
    })
    .then(res=>res.json())
    .then(data=>console.log(data))

fetch可以在header中设置CORS跨域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var u = new URLSearchParams();
u.append('method', 'flickr.interestingness.getList');
u.append('api_key', '<insert api key here>');
u.append('format', 'json');
u.append('nojsoncallback', '1');

fetch(url,{
method:'POST',
headers:u,
body:data
})
.then(res=>res.json())
.then(data=>console.log(data))



u.append("Access-Control-Allow-Origin", "*");
u.append("Access-Control-Allow-Headers", "X-Requested-With");
u.append("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
u.append("X-Powered-By",' 3.2.1')

ajax

  • 1.是XMLHTTPRequest的一个实例,需要new
  • 2.只有当状态为200或者304时才会请求成功
  • 3.格式零散,容易出现回调地狱的问题

fetch

  • 1.fetch是基于promise实现的,也可以结合async/await
  • 2.fetch请求默认是不带cookie的,需要设置fetch(URL,{credentials:’include’})。
  • Credentials有三种参数:same-origin,include,*
  • 3.服务器返回400 500 状态码时并不会reject,只有网络出错导致请求不能完成时,fetch才会被reject
  • 4.所有版本的 IE 均不支持原生 Fetch
  • 5.fetch是widow的一个方法,不需要new
  • 6.fetch 一直没有传统的 XHR 所拥有的 abort() ,setTimeout超时回调 和 onprogress 功能

fetch和ajax 的主要区别

  • 1、fetch()返回的promise将不会拒绝http的错误状态,即使响应是一个HTTP 404或者500
  • 2、在默认情况下 fetch不会接受或者发送cookies
  • 3、所有的IE浏览器都不会支持 fetch()方法
  • 4、服务器端返回 状态码 400 500的时候 不会reject
1
2
3
4
5
6
7
fetch(url).then(function (response) {
return response.json() //执行成功第一步
}).then(function (returnedValue) {
//执行成功的第二步
}).catch(function (err) {
//中途的任何地方出错 都会在这里被捕获到
})

fetch 的第二参数中
1、默认的请求为get请求 可以使用method:post 来进行配置
2、第一步中的 response有许多方法 json() text() formData()
3、Fetch跨域的时候默认不会带cookie 需要手动的指定 credentials:’include’

1
2
3
4
5
6
7
8
9
10
11
12
var promise=fetch('http://localhost:3000/news', {
method: 'get'
}).then(function(response) {
return response.json()
}).catch(function(err) {
// Error :(
});
promise.then(function (data) {
console.log(data)
}).catch(function (error) {
console.log(error)
})

fetch模拟abort请求 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥

超时设置
原生的XMLHttpRequest可以设置请求超时时间的方法回调;
ES6以后Promise 出现解决地狱回调等不优雅的代码风格。个人理解这个更像是一个生产者和消费者的关系,查看 Promise文档,有以下两个方法

  • Promise.race([promise1,promise2]) 传入多个Promise对象,等待最快对象完成
  • Promise.all([promise1,promise2]) 传入多个Promise 对象,等待所有对象完成

有了以上知识后,结合函数setTimeout就可以实现超时设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//ahutor:herbert qq:464884492
let timeoutPromise = (timeout) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("我是 timeoutPromise,已经完成了");
}, timeout);
});
}
let requestPromise = (url) => {
return fetch(url);
};
Promise.race([timeoutPromise(1000), requestPromise("https://www.baidu.com")])
.then(resp => {
console.log(resp);
})
.catch(error => {
console.log(error);
});

Ajax与Comet

可以检测XHR对象的readyState属性,该属性表示请求/响应过程的当前活动阶段

  • 0:未初始化。尚未调用open()方法
  • 1:启动。已经调用open()方法,但尚未调用send()方法
  • 2:发送。已经调用send()方法,但尚未接收到响应
  • 3:接收。已经接收到部分响应数据
  • 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
25
26
function createXHR() {
try {
return new XMLHttpRequest();
} catch (e) {
return new ActiveXObject("Microsoft.XMLHTTP");
}
}


var xhr = createXHR(); // return new XMLHttpRequest() or new ActiveXObject()
xhr.onreadystatechange = function(event){
// 不要使用this,作用域会产生问题,在部分浏览器中会执行失败
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
console.log(xhr.responseText);
} else {
console.log("Request was unsuccessful: " + xhr.status);
}
}
};


// xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

xhr.open("get", "example.txt", true);
xhr.send(null);

Ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 获取异步请求对象
function getXhr(){
var xhr=null;
if(window.XMLHttpRequest){
xhr=new XMLHttprequest();
}else{
// 如果浏览器版本是IE8以下浏览器
xhr=new ActiveXObject('Microsoft.XMLHttp');
}
return xhr;
}


// url:"url路径" type:请求方式 data:请求参数类型 dataType:返回的字符串类型
function ajax({url,type,data,dataType}){
return new Promise(function(open,err){
//1. 创建异步请求对象
var xhr=new XMLHttpRequest();
//2.绑定监听事件
xhr.onreadystatechange=function(){
// 当异步请求状态变为4时,并且返回的状态码为200,接收响应成功
if(xhr.readyState==4&&xhr.status==200){
// 当返回接收的字符串类型为json串时,自动转换json串
if(dataType!==undefined
&&dataType.toLowerCase()==="json")
var res=JSON.parse(xhr.responseText)
else
// 否则直接获取返回的响应文本中的内容
var res=xhr.responseText
// 通过Promise,将返回的数据向后传递
open(res);
}
}
// 如果请求方式为get请求,则将请求参数拼接在url后
if(type.toLowerCase()=="get"&&data!=undefined){
url+="?"+data;
}
//3.打开连接
xhr.open(type,url,true);
// 如果请求方式为post请求,则修改请求消息头
if(type.toLowerCase()==="post")
//增加:设置请求消息头
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
//4.发送请求
if(type.toLowerCase()=="post"&&data!==undefined)
xhr.send(data);
else
xhr.send(null);})

HTTP缓存机制(200还是304)

Expires Cache-Control

  • Cache-Control: no-cache 必须先与代理服务器确认是否更改,然后在在决定使用缓存还是请求,类似于协商缓存(304)
  • Cache-Control: no-store 才是真正的不缓存数据到本地
  • Cache-Control: public 可以被所有用户缓存(多用户共享),包括终端和CDN等中间代理服务器
  • Cache-Control: private 只能被终端浏览器缓存(而且是私有缓存),不允许中继缓存服务器进行缓存
  • Cache-Control: must-revalidate如果缓存内容失效,请求必须发送服务器进行验证
  • Cache-Control: max-age=s 缓存内容在s秒后失效,仅HTTP1.1可用

HTTP缓存机制及原理

1
2
3
4
5
6
Expires
Expires是http1.0提出的一个表示资源过期时间的header,它描述的是一个绝对时间,由服务器返回。
Expires第二次请求时,将和本地时间比对。
Expires 第一次请求服务器是,响应头会返回一个Expires的文件过期时间。
Expires 第二次请求,客户端使用本地时间和文件的过期时间进行比对,如果文件未过期则直接使用本地缓存,
返回状态码200(from memory cache)或200(from disk cache)。

BFC是什么

什么是BFC?

  • 根元素或其它包含它的元素
  • 浮动元素 (元素的 float 不是 none)
  • 绝对定位元素 (元素具有 position 为 absolute 或 fixed)
  • 内联块 (元素具有 display: inline-block)
  • 表格单元格 (元素具有 display: table-cell,HTML表格单元格默认属性)
  • 表格标题 (元素具有 display: table-caption, HTML表格标题默认属性)
  • 具有overflow 且值不是 visible 的块元素,
  • display: flow-root
  • column-span: all 应当总是会创建一个新的格式化上下文,即便具有 column-span: all 的元素并不被包裹在一个多列容器中。
  • 一个块格式化上下文包括创建它的元素内部所有内容,除了被包含于创建新的块级格式化上下文的后代元素内的元素。
1
2
3
4
5
6
哪些元素会生成BFC?
根元素
float属性不为none
position为absolute或fixed
display为inline-block, table-cell, table-caption, flex, inline-flex
overflow不为visible

BFC的布局规则 什么是BFC?看这一篇就够了

BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。

BFC是一个独立的布局环境,其中的元素布局是不受外界的影响,并且在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列。

  • 内部的Box会在垂直方向,一个接一个地放置。
  • Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠。
  • 每个盒子(块盒与行盒)的margin box的左边,与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。

  • BFC的区域不会与float box重叠。

  • BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
  • 计算BFC的高度时,浮动元素也参与计算。

如何创建BFC

1、float的值不是none。
2、position的值不是static或者relative。
3、display的值是inline-block、table-cell、flex、table-caption或者inline-flex
4、overflow的值不是visible

BFC的作用

1.利用BFC避免margin重叠。
2.自适应两栏布局
3.清楚浮动

🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥

React HOC的两种实践

从 0 到 1 实现 React 系列 —— 5.PureComponent 实现 && HOC 探幽

react高阶组件的理解

React进阶之高阶组件

  • 属性代理
  • 渲染劫持
  • 反向继承

存在两种高阶组件:

  • 属性代理—主要进行组件的复用
  • 反向集成【渲染的劫持】—主要进行渲染的劫持

箭头函数和普通函数的区别 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥

  • 箭头函数是匿名函数,不能作为构造函数,不能使用new
  • 箭头函数的 this 永远指向其上下文的 this ,任何方法都改变不了其指向,如 call() , bind() , apply()
  • 箭头函数通过 call() 或 apply() 方法调用一个函数时,只传入了一个参数,对 this 并没有影响。
  • 箭头函数不能绑定arguments,取而代之用rest参数…解决
  • 箭头函数没有原型属性
  • 头函数的this永远指向其上下文的this,没有办改变其指向,普通函数的this指向调用它的对象
  • 箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
  • 箭头函数不能当做Generator函数,不能使用yield关键字
  • 普通函数的this指向调用它的那个对象

fetch 的控制器和观察者 fetch 的控制器和观察者

因为受 cancelable promise 的拖延,fetch 一直没有传统的 XHR 所拥有的 abort() 和 onprogress 功能,去年年底 cancelable promise 草案被彻底废弃了,所以今年年初的这几个月里,一份完全新的、fetch 专用的、不依赖 JS 规范、与 Promise 没有任何直接关系的草案诞生了,而且 Firefox 已经率先实现了其中的大部分功能。

这份草案中引入了 3 种新的对象类型,FetchController(fetch 控制器)、FetchSignal(fetch 信号)、FetchObserver(fetch 观察者),虽然这三个都是全局构造函数,但其实只有 FetchController 对象是真的需要你手动 new 出来的,其它两种对象都是浏览器为你生成的,我们下面分别演示下如何中断(取消、中止)一个 fetch 请求,以及如何监听 fetch 请求的执行进度。

中断 fetch 请求

1
2
3
4
5
6
7
8
9
10
11
12
const controller = new FetchController()
const signal = controller.signal

fetch("https://tc39.github.io/ecma262/", { signal }).then(() => {
alert("请求成功")
}).catch(err => {
if (err.name === "AbortError") {
alert("请求被中断")
}
})

setTimeout(() => controller.abort(), 500)

这段代码中,我们先创建了一个 fetch 控制器 controller,每个控制器都自带一个 fetch 信号对象(signal 属性),然后在你用 fetch() 发送请求的时候把这个信号对象带上(signal 参数)。之后,你就可以通过控制器的 abort() 方法,来中断那个 fetch 请求了。只要在你执行 abort() 方法的时候,那个 fetch 请求还没有执行完毕(请求还没发出去、请求发出去了还没接收到响应、响应还没接收完),那么当初 fetch() 返回的那个 promise 就会被 reject,所产生的 error 对象的 name 为 AbortError。

上面这个例子中,我是在 500 毫秒的时候中断了这个请求,这是个绝对的数字,所以根据你的网速不同,执行代码时你有可能会看到弹出“请求成功”,也有能看到“请求被中断”。

一个信号对象还可以同时传递给多个 fetch 请求:

1
2
3
4
5
6
7
8
const controller = new FetchController()
const signal = controller.signal

fetch("https://tc39.github.io/ecma262/", { signal })
fetch("https://fetch.spec.whatwg.org/", { signal })
fetch("https://dom.spec.whatwg.org/", { signal })

controller.abort() // 同时中断三个请求

信号对象有一个 aborted 属性表明自己是否已经被中断过了,一旦被中断,它不可能回到当初的状态,也就是说,你不能二次利用一个已经被中断过的信号对象,把这样的信号对象传给 fetch() 的话,fetch() 会立即结束,不会发出请求:

1
2
3
4
5
6
7
8
const controller = new FetchController()
const signal = controller.signal

controller.abort()

alert(signal.aborted) // true

fetch("https://tc39.github.io/ecma262/", { signal }) // 直接被 reject,请求不会发出

信号对象上还可以监听 abort 事件,不过我想一般用不上,因为请求中断后的处理代码一般写在 fetch(…).catch(…) 里面:

1
2
3
4
5
6
7
8
9
10
11
const controller = new FetchController()
const signal = controller.signal

signal.addEventListener("abort", function(e) { // 使用 signal.onabort = 注册监听函数也可以
alert(e.type) // abort
alert(signal.aborted) // true
})

alert(signal.aborted) // false

controller.abort()

一个控制器除了可以让自己的信号对象 abort,还可以关注(跟随、监听)一个别的控制器的信号对象,只要关注的这个信号对象被 abort 了,自己的 abort() 方法就会被自动执行,从而自己的信号对象也会被 abort:

1
2
3
4
5
6
7
8
const fc1 = new FetchController()
const fc2 = new FetchController()

fc1.follow(fc2.signal)

fc2.abort()

alert(fc1.signal.aborted) // true,即便你没有手动执行 fc1.abort()

再来看个更复杂的例子:

1
2
3
4
5
6
7
8
9
10
11
12
const fc1 = new FetchController()
const fc2 = new FetchController()
const fc3 = new FetchController()

fc1.follow(fc2.signal)
fc2.follow(fc3.signal)

fetch("https://tc39.github.io/ecma262/", { signal: fc1.signal })
fetch("https://fetch.spec.whatwg.org/", { signal: fc2.signal })
fetch("https://dom.spec.whatwg.org/", { signal: fc3.signal })

fc3.abort() // 三个请求都中断了

一个控制器只能关注一个别人家的信号对象,而一个信号对象可以被任意多个别家的控制器关注。不能直接或间接的关注自己的信号对象,比如上面的代码中再增加一行 fc3.follow(fc1.signal) 就会产生环形关注,所以那句代码会没有任何效果。还有一个 unfollow() 方法可以取消关注。

总结下就是,一个控制器对象有一个 signal 属性指向一个信号对象,还有 abort()、follow()、unfollow() 这三个方法。一个信号对象有一个 aborted 属性,还有一个 onabort 事件监听函数。

监听 fetch 请求

上面说到了 FetchController 和 FetchSignal,接下来要说说 fetch 观察者 - FetchObserver 对象。fetch 观察者对象上可以监听 statechange、requestprogress、responseprogress 三种事件,分别用来获取 fetch 请求的状态信息变化、请求数据发送的大小变化(POST 请求)、响应数据接收的大小变化信息,先来看看如何监听状态信息的变化:

1
2
3
4
5
6
7
fetch("https://tc39.github.io/ecma262/", {
observe(observer) {
observer.onstatechange = () => { // 也可以用 addEventListener("statechange"...
alert(observer.state) // "requesting"、"responding"、"aborted", "errored"、"complete" 中的某个值
}
}
})

FetchObserver 对象不用你手动 new,浏览器会为你 new 一个,在 fetch 请求开始之前,通过你事先指定的 observe 回调函数的参数传递给你。然后你在这个观察者对象上注册 statechange 监听函数,就可以在请求的不同阶段执行特定的代码了。

监听响应的接收进度使用 responseprogress 事件:

1
2
3
4
5
6
7
8
9
fetch("https://img.alicdn.com/tfscom/TB1y6icQXXXXXaQXFXXXXXXXXXX.mp4", {
observe(observer) {
observer.onresponseprogress = e => {
if(e.lengthComputable) {
console.log("视频下载进度:" + parseInt(e.loaded / e.total * 100) + "%")
}
}
}
})

监听 POST 数据的发送进度使用 requestprogress 事件:

1
2
3
4
5
6
7
8
9
10
fetch("/upload", {
method: "POST",
credentials: "include",
body: canvas.toBlob(),
observe(observer) {
observer.onrequestprogress = e => {
console.log("图片上传进度:" + parseInt(e.loaded / e.total * 100) + "%")
}
}
})

在本文发布的现在,规范草案还只是最初的阶段,可能有很多细节没考虑,目前 Firefox 也没有实现我上面所说的所有功能,实现了的也有一些 bug,而且还需要事先在 about:config 手动添加某两个选项才可以使用,所以本文仅仅是简要介绍,大家暂时眼睛看看就够了。

以后 fetch 控制器的功能可能还会增加,不仅仅能让一个 fetch 请求中断,比如还可以指定这个请求的优先级,fetch 观察者同时也可以用来监听 fetch 请求优先级的变化。

fetch默认不携带cookie

fetch发送请求默认是不发送cookie的,不管是同域还是跨域;那么问题就来了,对于那些需要权限验证的请求就可能无法正常获取数据,这时可以配置其credentials项,其有3个值:

  • omit: 默认值,忽略cookie的发送
  • same-origin: 表示cookie只能同域发送,不能跨域发送
  • include: cookie既可以同域发送,也可以跨域发送

credentials所表达的含义,其实与XHR2中的withCredentials属性类似,表示请求是否携带cookie;具体可以参考阮一峰老师的跨域资源共享 CORS 详解中withCredentials一节的介绍;
若要fetch请求携带cookie信息,只需设置一下credentials选项即可,例如

1
fetch(url, {credentials: 'include'})

fetch请求对某些错误http状态不会reject

这主要是由fetch返回promise导致的,因为fetch返回的promise在某些错误的http状态下如400 、500等不会reject,相反它会被resolve;只有网络错误会导致请求不能完成时,fetch 才会被 reject;所以一般会对fetch请求做一层封装,例如下面代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
}
const error = new Error(response.statusText);
error.response = response;
throw error;
}
function parseJSON(response) {
return response.json();
}
export default function request(url, options) {
let opt = options||{};
return fetch(url, {credentials: 'include', ...opt})
.then(checkStatus)
.then(parseJSON)
.then((data) => ( data ))
.catch((err) => ( err ));
}

fetch不支持超时timeout处理

用过fetch的都知道,fetch不像大多数ajax库那样对请求设置超时timeout,它没有有关请求超时的feature,这一点比较蛋疼。所以在fetch标准添加超时feature之前,都需要polyfill该特性。

实际上,我们真正需要的是abort(), timeout可以通过timeout+abort方式来实现,起到真正超时丢弃当前的请求。

而在目前的fetch指导规范中,fetch并不是一个具体实例,而只是一个方法;其返回的promise实例根据Promise指导规范标准是不能abort的,也不能手动改变promise实例的状态,只能由内部来根据请求结果来改变promise的状态。

既然不能手动控制fetch方法执行后返回的promise实例状态,那么是不是可以创建一个可以手动控制状态的新Promise实例呢。所以:

方法一:单纯setTimeout方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var oldFetchfn = fetch; //拦截原始的fetch方法
window.fetch = function(input, opts){//定义新的fetch方法,封装原有的fetch方法
return new Promise(function(resolve, reject){
var timeoutId = setTimeout(function(){
reject(new Error("fetch timeout"))
}, opts.timeout);
oldFetchfn(input, opts).then(
res=>{
clearTimeout(timeoutId);
resolve(res)
},
err=>{
clearTimeout(timeoutId);
reject(err)
}
)
})
}

方法二:利用Promise.race方法

Promise.race方法接受一个promise实例数组参数,表示多个promise实例中任何一个最先改变状态,那么race方法返回的promise实例状态就跟着改变,具体可以参考这里。

1
2
3
4
5
6
7
8
9
10
var oldFetchfn = fetch; //拦截原始的fetch方法
window.fetch = function(input, opts){//定义新的fetch方法,封装原有的fetch方法
var fetchPromise = oldFetchfn(input, opts);
var timeoutPromise = new Promise(function(resolve, reject){
setTimeout(()=>{
reject(new Error("fetch timeout"))
}, opts.timeout)
});
retrun Promise.race([fetchPromise, timeoutPromise])
}

fetch不支持取消请求

我们可以看看AbortController

首先通过AbortController() 构造函数来创建一个controller实例, 然后通过AbortController.signal 属性获取到它的关联对象AbortSignal 的引用.

当 fetch request 初始化后, 将 AbortSignal 作为一个选项传入请求的选项参数中 (如下 {signal}). 这将signal,controller与fetch请求关联起来, 允许我们通过调用AbortController.abort()来取消fetch请求, 正如下第二个事件监听器所示.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 var oldFetchfn = fetch; 
var controller = new AbortController();
var signal = controller.signal;

var downloadBtn = document.querySelector('.download');
var abortBtn = document.querySelector('.abort');

downloadBtn.addEventListener('click', fetchVideo);

abortBtn.addEventListener('click', function() {
controller.abort();
console.log('Download aborted');
});

function fetchVideo() {
...
fetch(url, {signal}).then(function(response) {
...
}).catch(function(e) {
reports.textContent = 'Download error: ' + e.message;
})
}

Fetch —— 中止尚未完成的接口请求