1. 程式人生 > >僅僅知道如何終止XHR請求,或許對你來說是不夠的!

僅僅知道如何終止XHR請求,或許對你來說是不夠的!

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的基本定義:

  • 接受一個必須的引數和一個可選的引數
    1. 定義要獲取的資源,地址或者Request物件
    2. 可選的配置物件,比如請求方式、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我們可以發現,其實主流瀏覽器大部分都支援了,如果我們開發的平臺很新還是可以使用的,相信不遠的將來,肯定會大批量使用。前端的道路也會越來越順暢!

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