TLDR:
當我們需要的時候,我們可以通過AbortController接口來終止一個或者多個請求。
前言
到目前為止,我們有兩個常用的基本的手段去發送請求進而局部刷新頁面內容,其一是XMR(XMLHttpRequest),其二是fetch,我們一個個說
XHR
對於XHR,我們或許已經很熟悉了,當我們想要發送一個請求的時候,我們可以這樣做:
const xhr = new XMLHttpRequest();
const method = 'GET';
const url = 'https://xxx';
xhr.open(method, url, true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
// do something
}
}
xhr.send();
當我們由於某種原因(比如重復請求)想要終止它的時候,我們只需要調用abort即可。
xhr.abort();
很方便也很簡潔,但是對於fetch呢?
fetch
首先我們看下fetch的基本定義:
-
接受一個必須的參數和一個可選的參數
- 定義要獲取的資源,地址或者Request對象
- 可選的配置對象,比如請求方式、body、credentials等等,其中我們需要知道的是
signal,他的定義如下:
一個AbortSignal對象實例,允許你通過AbortController與fetch請求通信或者終止fetch
-
返回值是一個promise
看到這里我們已經知道了答案,但是我們需要再去了解一下上文所說的AbortController.
AbortController
最初es6引入fetch的時候,其實就是沒有abort這樣的功能,不過廣大程序朋友們還會希望能有這個靈活的api,所以在2015年就有人提了這個issue,再次之后大家嘗試了注入promise式的取消或者是其他hack等等,經過這份折騰最終我們迎來了AbortController和AbortSignal。
AbortController目前很簡單,有一個制度的屬性AbortController.signal和一個用來中斷請求的.abort()
光說也沒啥意思,咱看代碼說話:
// 啟動一個node服務,其中包括一個api和一個html頁面
const Koa = require('koa');
const fs = require('fs');
const app = new Koa();
const sleep = () => {
return new Promise(res => {
setTimeout(function() {
res();
}, 3000);
});
};
app.use(async ctx => {
if (ctx.request.url === '/api') {
await sleep();
ctx.body = 'Hello World';
} else {
ctx.status = 200;
ctx.respond = false;
fs.createReadStream('./test.html').pipe(ctx.res);
}
});
app.listen(3000);
下面是test.html的內容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
fetch('/api')
.then((res) => {
console.log(res, '請求成功');
});
</script>
</body>
</html>
啟動服務后,我們看下network的內容。

我們注意兩個地方,一個代表fetch請求,一個代表請求的延時時間,也就是我們定義的三秒
取消fetch
這時候我們想中斷,就可以這樣做:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 增加了如下幾行
const controller = new AbortController();
const signal = controller.signal;
console.log(signal, 'signal的初始狀態');
signal.addEventListener('abort', function (e) {
console.log(signal, 'signal的中斷狀態');
});
setTimeout(function() {
controller.abort();
}, 2000);
// 增加部分結束
fetch('/api', {signal})
.then((res) => {
console.log(res, '請求成功');
});
</script>
</body>
</html>
再次運行,我們會得到如下結果:


從圖中我們可以很清楚的看到,請求在2s后被終止,請求狀態變為canceled,然后aborted的狀態由false轉變為true。
就是這樣,我們對fetch也進行的取消操作,還算是豁然開朗吧。嘻嘻。
兼容性
雖然AbortController已經誕生很長時間了,但是目前mdn上的定義還是實驗性技術,查看mdn我們可以發現,其實主流瀏覽器大部分都支持了,如果我們開發的平台很新還是可以使用的,相信不遠的將來,肯定會大批量使用。前端的道路也會越來越順暢!

最后如果這邊文章能幫給你帶來一點幫助,歡迎關注,點贊,制作不易,與君共勉!

