欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript前端超時(shí)異步操作完美解決過(guò)程

 更新時(shí)間:2021年11月25日 11:16:00   作者:廈門(mén)在乎科技  
這篇文章主要為大家介紹了JavaScript前端超時(shí)異步操作的完美解決方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步

自從 ECMAScript 的 Promise?ES2015?和 async/await?ES2017?特性發(fā)布以后,異步在前端界已經(jīng)成為特別常見(jiàn)的操作。異步代碼和同步代碼在處理問(wèn)題順序上會(huì)存在一些差別,編寫(xiě)異步代碼需要擁有跟編寫(xiě)同步代碼不同的“意識(shí)”。

如果一段代碼久久不能執(zhí)行完成,會(huì)怎么樣?

如果這是同步代碼,我們會(huì)看到一種叫做“無(wú)響應(yīng)”的現(xiàn)象,或者通俗地說(shuō) —— “死掉了”;但是如果是一段異步代碼呢?可能我們等不到結(jié)果,但別的代碼仍在繼續(xù),就好像這件事情沒(méi)有發(fā)生一般。

當(dāng)然事情并不是真的沒(méi)發(fā)生,只不過(guò)在不同的情況下會(huì)產(chǎn)生不同的現(xiàn)象。比如有加載動(dòng)畫(huà)的頁(yè)面,看起來(lái)就是一直在加載;又比如應(yīng)該進(jìn)行數(shù)據(jù)更新的頁(yè)面,看不到數(shù)據(jù)變化;

再比如一個(gè)對(duì)話(huà)框,怎么也關(guān)不掉 …… 這些現(xiàn)象我們統(tǒng)稱(chēng)為 BUG。但也有一些時(shí)候,某個(gè)異步操作過(guò)程并沒(méi)有“回顯”,它就默默地死在那里,沒(méi)有人知道,待頁(yè)面刷新之后,就連一點(diǎn)遺跡都不會(huì)留下。

Axios 自帶超時(shí)處理

使用 Axios 進(jìn)行 Web Api 調(diào)用就是一種常見(jiàn)的異步操作過(guò)程。通常我們的代碼會(huì)這樣寫(xiě):

try {
    const res = await axios.get(url, options);
    // TODO 正常進(jìn)行后續(xù)業(yè)務(wù)
} catch(err) {
    // TODO 進(jìn)行容錯(cuò)處理,或者報(bào)錯(cuò)
}

這段代碼一般情況下都執(zhí)行良好,直到有一天用戶(hù)抱怨說(shuō):怎么等了半天沒(méi)反應(yīng)?

然后開(kāi)發(fā)者意識(shí)到,由于服務(wù)器壓力增大,這個(gè)請(qǐng)求已經(jīng)很難瞬時(shí)響應(yīng)了??紤]到用戶(hù)的感受,加了一個(gè) loading 動(dòng)畫(huà):

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

然而有一天,有用戶(hù)說(shuō):“我等了半個(gè)小時(shí),居然一直在那轉(zhuǎn)圈圈!”于是開(kāi)發(fā)者意識(shí)到,由于某種原因,請(qǐng)求被卡死了,這種情況下應(yīng)該重發(fā)請(qǐng)求,或者直接報(bào)告給用戶(hù) —— 嗯,得加個(gè)超時(shí)檢查。

幸運(yùn)的是 Axios 確實(shí)可以處理超時(shí),只需要在?options?里添加一個(gè)?timeout: 3000?就能解決問(wèn)題。如果超時(shí),可以在?catch?塊中檢測(cè)并處理:

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

Axios 沒(méi)問(wèn)題了,如果用?fetch()?呢?

處理 fetch() 超時(shí)

fetch()?自己不具備處理超時(shí)的能力,需要我們判斷超時(shí)后通過(guò)?AbortController?來(lái)觸發(fā)“取消”請(qǐng)求操作。

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

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

ac.abort()?會(huì)向?signal?發(fā)送信號(hào),觸發(fā)它的?abort?事件,并將其?.aborted?屬性置為?true。fetch()?內(nèi)部處理會(huì)利用這些信息中止掉請(qǐng)求。

上面這個(gè)示例演示了如何實(shí)現(xiàn)?fetch()?操作的超時(shí)處理。如果使用?await?的形式來(lái)處理,需要把?setTimeout(...)?放在?fetch(...)?之前:

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

為了避免使用?try ... catch ...?來(lái)處理請(qǐng)求失敗,這里在?fetch()?后加了一個(gè)?.catch(...)?在忽略錯(cuò)誤的情況。如果發(fā)生錯(cuò)誤,res?會(huì)被賦值為?undefined。實(shí)際的業(yè)務(wù)處理可能需要更合理的?catch()?處理來(lái)讓?res?包含可識(shí)別的錯(cuò)誤信息。

本來(lái)到這里就可以結(jié)束了,但是對(duì)每一個(gè)?fetch()?調(diào)用都寫(xiě)這么長(zhǎng)一段代碼,會(huì)顯得很繁瑣,不如封裝一下:

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

沒(méi)問(wèn)題了嗎?不,有問(wèn)題。

如果我們?cè)谏鲜龃a的?setTimeout(...)?里輸出一條信息:

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

并且在調(diào)用的給一個(gè)足夠的時(shí)間:

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

我們會(huì)看到輸出?success,并在 5 秒后看到輸出?It's timeout。

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

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

完美!但問(wèn)題還沒(méi)結(jié)束。

萬(wàn)物皆可超時(shí)

Axios 和 fetch 都提供了中斷異步操作的途徑,但對(duì)于一個(gè)不具備?abort?能力的普通 Promise 來(lái)說(shuō),該怎么辦?

對(duì)于這樣的 Promise,我只能說(shuō),讓他去吧,隨便他去干到天荒地老 —— 反正我也沒(méi)辦法阻止。但生活總得繼續(xù),我不能一直等?。?/p>

這種情況下我們可以把?setTimeout()?封裝成一個(gè) Promise,然后使用?Promise.race()?來(lái)實(shí)現(xiàn)“過(guò)時(shí)不候”:

race 是競(jìng)速的意思,所以?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
}

可以寫(xiě)一個(gè) Timeout 來(lái)模擬看看效果:

(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前端超時(shí)異步操作完美解決的詳細(xì)內(nèi)容,更多關(guān)于解決前端超時(shí)的異步操作的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論