1. 程式人生 > >Fetch超時設定和終止請求

Fetch超時設定和終止請求

1.基本使用

Fetch 是一個新的端獲取資源的介面,用於替換笨重繁瑣XMLHttpRequest.它有了Request 和 Response 以及Headers物件的概念,與後端語言請求資源更接近。

  • 一個簡單的GET請求

    fetch('https://www.baidu.com')
            .then(resp=>resp.text())  //  轉換成文字物件
            .then(resp=>console.log(resp))  // 輸出請求內容
            .catch(error => console.error(error));
  • 一個簡單的POST請求

    fetch('https://www.easy-mock.com/mock/5ca59ba44ba86c23d507bd40/example/getUser',{method:"post"})
            .then(resp=>resp.json())  //轉換成Json物件
            .then(resp=>console.log(resp)) //輸出Json內容
            .catch(error => console.error(error));

    更多Fetch相關詳細,可檢視MDN文件 developer.mozilla.org/en-US/docs/…

2.超時設定

在使用XMLHttpRequest可以設定請求超時時間,可是轉用Fetch後,超時時間設定不見了,在網路不可靠的情況下,超時設定往往很有用

ES6以後Promise 出現解決地獄回撥等不優雅的程式碼風格。個人理解這個更像是一個生產者和消費者的關係,檢視 Promise文件,有以下兩個方法

  1. Promise.race([promise1,promise2]) 傳入多個Promise物件,等待最快物件完成
  2. Promise.all([promise1,promise2]) 傳入多個Promise 物件,等待所有物件完成

有了以上知識後,結合函式setTimeout就可以實現超時設定

//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);
    });

3.取消請求

將上邊的程式碼拷貝的瀏覽器控制檯並將network設定為Slow3G。執行就會發現,雖然我們在控制檯看到了超時資訊,但切換到netwok頁籤中發現請求依然正常進行中,並返回了正確的內容。這並不是我想要的結果,我希望超時時間到了,請求也應該終止。

fetch請求成功後,預設返回一個Response物件,那麼我們如何在程式碼中構造一個這樣的物件呢?

  timeoutResp=new Response("timeout", { status: 504, statusText: "timeout " })
  successResp=new Response("ok", { status: 200, statusText: "ok " })

AbortController 用於手動終止一個或多個DOM請求,通過該物件的AbortSignal注入的Fetch的請求中。所以需要完美實現timeout功能加上這個就對了

//ahutor:herbert qq:464884492
let controller = new AbortController();
let signal = controller.signal;

let timeoutPromise = (timeout) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(new Response("timeout", { status: 504, statusText: "timeout " }));
            controller.abort();
        }, timeout);
    });
}
let requestPromise = (url) => {
    return fetch(url, {
        signal: signal
    });
};
Promise.race([timeoutPromise(1000), requestPromise("https://www.baidu.com")])
    .then(resp => {
        console.log(resp);
    })
    .catch(error => {
        console.log(error);
    });

4.總結

第一次在專案中使用fetch,在面向API程式設計的過程中,發現fetch沒有超時的設定。第一時間查看了MDN文件以及向搜尋引擎找尋實現功能的靈感(copy+c)。有些朋友在settimeout中通過 reject(new Error('網路超時'))實現。其實這樣只是讓前端感知當前請求超時了,並沒有真正終止本次請求。所以必須藉助AbortSignal訊號物件。此功能目前還處於試驗階段,使用需謹慎。

demo地址 https://github.com/464884492/blog/blob/master/demo/fetch/fetchdem