91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JavaScript前端超時異步操作的解決方法

發布時間:2021-11-25 13:18:39 來源:億速云 閱讀:230 作者:柒染 欄目:開發技術

今天就跟大家聊聊有關JavaScript前端超時異步操作的解決方法,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

自從 ECMAScript 的 Promise ES2015 和 async/await ES2017 特性發布以后,異步在前端界已經成為特別常見的操作。異步代碼和同步代碼在處理問題順序上會存在一些差別,編寫異步代碼需要擁有跟編寫同步代碼不同的“意識”。

如果一段代碼久久不能執行完成,會怎么樣?

如果這是同步代碼,我們會看到一種叫做“無響應”的現象,或者通俗地說 —— “死掉了”;但是如果是一段異步代碼呢?可能我們等不到結果,但別的代碼仍在繼續,就好像這件事情沒有發生一般。

當然事情并不是真的沒發生,只不過在不同的情況下會產生不同的現象。比如有加載動畫的頁面,看起來就是一直在加載;又比如應該進行數據更新的頁面,看不到數據變化;

再比如一個對話框,怎么也關不掉 …… 這些現象我們統稱為 BUG。但也有一些時候,某個異步操作過程并沒有“回顯”,它就默默地死在那里,沒有人知道,待頁面刷新之后,就連一點遺跡都不會留下。

Axios 自帶超時處理

使用 Axios 進行 Web Api 調用就是一種常見的異步操作過程。通常我們的代碼會這樣寫:

try {
    const res = await axios.get(url, options);
    // TODO 正常進行后續業務
} catch(err) {
    // TODO 進行容錯處理,或者報錯
}

這段代碼一般情況下都執行良好,直到有一天用戶抱怨說:怎么等了半天沒反應?

然后開發者意識到,由于服務器壓力增大,這個請求已經很難瞬時響應了。考慮到用戶的感受,加了一個 loading 動畫:

try {
    showLoading();
    const res = await axios.get(url, options);
    // TODO 正常業務
} catch (err) {
    // TODO 容錯處理
} finally {
    hideLoading();
}

然而有一天,有用戶說:“我等了半個小時,居然一直在那轉圈圈!”于是開發者意識到,由于某種原因,請求被卡死了,這種情況下應該重發請求,或者直接報告給用戶 —— 嗯,得加個超時檢查。

幸運的是 Axios 確實可以處理超時,只需要在 options 里添加一個 timeout: 3000 就能解決問題。如果超時,可以在 catch 塊中檢測并處理:

try {...}
catch (err) {
    if (err.isAxiosError && !err.response && err.request
        && err.message.startsWith("timeout")) {
        // 如果是 Axios 的 request 錯誤,并且消息是延時消息
        // TODO 處理超時
    }
}
finally {...}

Axios 沒問題了,如果用 fetch() 呢?

處理 fetch() 超時

fetch() 自己不具備處理超時的能力,需要我們判斷超時后通過 AbortController 來觸發“取消”請求操作。

如果需要中斷一個 fetch() 操作,只需從一個 AbortController 對象獲取 signal,并將這個信號對象作為 fetch() 的選項傳入。大概就是這樣:

const ac = new AbortController();
const { signal } = ac;
fetch(url, { signal }).then(res => {
    // TODO 處理業務
});
 
// 1 秒后取消 fetch 操作
setTimeout(() => ac.abort(), 1000);

ac.abort() 會向 signal 發送信號,觸發它的 abort 事件,并將其 .aborted 屬性置為 truefetch() 內部處理會利用這些信息中止掉請求。

上面這個示例演示了如何實現 fetch() 操作的超時處理。如果使用 await 的形式來處理,需要把 setTimeout(...) 放在 fetch(...) 之前:

const ac = new AbortController();
const { signal } = ac;
setTimeout(() => ac.abort(), 1000);
const res = await fetch(url, { signal }).catch(() => undefined);

為了避免使用 try ... catch ... 來處理請求失敗,這里在 fetch() 后加了一個 .catch(...) 在忽略錯誤的情況。如果發生錯誤,res 會被賦值為 undefined。實際的業務處理可能需要更合理的 catch() 處理來讓 res 包含可識別的錯誤信息。

本來到這里就可以結束了,但是對每一個 fetch() 調用都寫這么長一段代碼,會顯得很繁瑣,不如封裝一下:

async function fetchWithTimeout(timeout, resoure, init = {}) {
    const ac = new AbortController();
    const signal = ac.signal;
    setTimeout(() => ac.abort(), timeout);
    return fetch(resoure, { ...init, signal });
}

沒問題了嗎?不,有問題。

如果我們在上述代碼的 setTimeout(...) 里輸出一條信息:

setTimeout(() => {
    console.log("It's timeout");
    ac.abort();
}, timeout);

并且在調用的給一個足夠的時間:

fetchWithTimeout(5000, url).then(res => console.log("success"));

我們會看到輸出 success,并在 5 秒后看到輸出 It's timeout

對了,我們雖然為 fetch(...) 處理了超時,但是并沒有在 fetch(...) 成功的情況下干掉 timer。作為一個思維縝密的程序員,怎么能夠犯這樣的錯誤呢?干掉他!

async function fetchWithTimeout(timeout, resoure, init = {}) {
    const ac = new AbortController();
    const signal = ac.signal;    
    const timer = setTimeout(() => {
        console.log("It's timeout");
        return ac.abort();
    }, timeout);    
    try {
        return await fetch(resoure, { ...init, signal });
    } finally {
        clearTimeout(timer);
    }
}

完美!但問題還沒結束。

萬物皆可超時

Axios 和 fetch 都提供了中斷異步操作的途徑,但對于一個不具備 abort 能力的普通 Promise 來說,該怎么辦?

對于這樣的 Promise,我只能說,讓他去吧,隨便他去干到天荒地老 —— 反正我也沒辦法阻止。但生活總得繼續,我不能一直等啊!

這種情況下我們可以把 setTimeout() 封裝成一個 Promise,然后使用 Promise.race() 來實現“過時不候”:

race 是競速的意思,所以 Promise.race() 的行為是不是很好理解?

function waitWithTimeout(promise, timeout, timeoutMessage = "timeout") {
    let timer;
    const timeoutPromise = new Promise((_, reject) => {
        timer = setTimeout(() => reject(timeoutMessage), timeout);
    }); 
    return Promise.race([timeoutPromise, promise])
        .finally(() => clearTimeout(timer));    // 別忘了清 timer
}

可以寫一個 Timeout 來模擬看看效果:

(async () => {
    const business = new Promise(resolve => setTimeout(resolve, 1000 * 10));
    try {
        await waitWithTimeout(business, 1000);
        console.log("[Success]");
    } catch (err) {
        console.log("[Error]", err);    // [Error] timeout
    }
})();

看完上述內容,你們對JavaScript前端超時異步操作的解決方法有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

上杭县| 老河口市| 高州市| 手机| 麻阳| 彩票| 名山县| 柘城县| 新民市| 武川县| 平昌县| 江山市| 武威市| 五大连池市| 赤峰市| 鄄城县| 改则县| 香河县| 青阳县| 长沙市| 白河县| 治多县| 克什克腾旗| 蓬莱市| 天门市| 鲁山县| 五莲县| 都昌县| 伊金霍洛旗| 洛阳市| 杨浦区| 柏乡县| 台湾省| 若尔盖县| 仁寿县| 淄博市| 徐闻县| 定襄县| 正宁县| 江都市| 永州市|