如何中斷一個正在發出的請求

hi_chegde發表於2018-04-12

在使用者互動或者最新輸入的時候,你常常需要在一個 web 應用中頻繁發起請求。比如說在輸入文字的時候的自動完成操作或者是在地圖上放大縮小的操作。讓我們花一點時間去思考一下這些例子。首先是自動完成,每一次我們輸入的時候(或者更少在用 debounce 時)我們就會發出一個請求。如果使用者輸入改變的時候,舊的請求就會與現在無關,當我們持續打字輸入的時候(比如 “java” 和 “javascript” )。也許有很多冗餘請求,在我們拿到我們需要的東西的時候。

然後是地圖的例子。我們在縮放檢視地圖的時候,我們對於之前縮放級別的圖塊不再感興趣。與此同時,為了冗餘資料,許多請求還處於等待。

帶著我們第一個例子,看看實現一個自動完成場景的原生程式碼。為了本文的目的,我們將使用更現代的 fetch 而不是 XMLHttpRequest 來提出網路請求。 程式碼如下:

autocompleteInput.addEventListener('keydown', function() {

    const url = "https://api.example.com/autocomplete"

    fetch(url)
        .then((response) => {
            // Do something with the response
            updateAutocompleteMenu()
        })
        .catch((error) => {
            // Something went wrong
            handleAutocompleteError(error);
        })

});
複製程式碼

這個例子的問題就是每一個請求都要完成,即使它不再相關。我們可以實現一個額外的邏輯在 updateAutocompeleteMenu 中去防止額外的程式碼,但是卻不能真正停止這個請求。值得注意的是,瀏覽器有同時請求的限制,這意味著一旦限制被觸發時,請求將佇列化(而且每個瀏覽器限制又不同)。

Abortable Fetch

我們可以用來解決上述問題的新瀏覽器技術是 Abortable Fetch 。 Abortable Fetch 依賴瀏覽器的規範 AbortController 。這個控制器有 signal 屬性,我們用它傳遞,在之後的時機中用控制器中斷方法去取消請求。

一個例子如下:

autocompleteInput.addEventListener('keydown', function() {
const url = "https://api.example.com/autocomplete"
let controller;
let signal;

autocompleteInput.addEventListener('keyup', () => {

    if (controller !== undefined) {
        // Cancel the previous request
        controller.abort();
    }

    // Feature detect
    if ("AbortController" in window) {
        controller = new AbortController;
        signal = controller.signal;
    }

    // Pass the signal to the fetch request
    fetch(url, {signal})
        .then((response) => {
            // Do something with the response
            updateAutocompleteMenu()
        })
        .catch((error) => {
            // Something went wrong
            handleAutocompleteError(error);
        })
    });

});

複製程式碼

我們進行特性檢測,以確定我們是否可以使用 AbortController (它在 Edge,Firefox,Opera 和 Chrome 66 中支援!)。我們確認是否有一個控制器已經建立,如果是,我們呼叫 controller.abort() 去取消之前的請求。你也可以一次用相同的 signal 取消多個 fetches 。

一個小例子

我建立了一個展示如何使用 Abortable Fetch 小例子,基於自動完成場景的想法(沒有任何實現細節!)。去理解每當輸入網路請求時會發生什麼。 如果您在舊請求完成之前進行了新的輸入,它將中止先前的抓取。 在實踐中它看起來有點像這樣:

abortable fetch

原始碼

深入思考

也許 AbortController 最酷的部分是它被設計成一箇中止非同步任務的通用機制。它是 WHATWG specification 的一部分。這意味著它是DOM規範而不是語言(ECMAScript)規範,但對於前端開發來說,這仍然是一個有用的功能。你可以利用它作為一個更清晰的非同步控制流機制,用於實現非同步任務的時候(即當使用 Promises 時)。這篇Bram Van Damme super article for a more detailed example 文章會有更多我所講述的細節。

本翻譯自原文 - Cancelling Requests with Abortable Fetch。不定期更新技術博文歡迎star。由於本人水平有限,錯誤之處在所難免,敬請指正!轉載請註明出處,保留原文連結以及作者資訊。

相關文章