日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
如何優(yōu)雅地中斷Promise?

大家在平時(shí)的開(kāi)發(fā)過(guò)程中估計(jì)不會(huì)經(jīng)常碰到需要主動(dòng)取消一個(gè) Fetch 請(qǐng)求的需求,所以一部分同學(xué)可能對(duì)這一塊知識(shí)不是很了解。沒(méi)有關(guān)系,看完這篇文章你就能夠掌握關(guān)于如何終止一個(gè) Fetch 請(qǐng)求或者一個(gè) Promise 的全部技能了。那我們趕快開(kāi)始吧~

這篇文章比我預(yù)期要花費(fèi)的時(shí)間和精力還要多,所以文章比較長(zhǎng),大家現(xiàn)在沒(méi)時(shí)間瀏覽的可以先收藏起來(lái),以后慢慢看。如果覺(jué)得這篇文章不錯(cuò)的話,也可以幫忙點(diǎn)個(gè)贊,轉(zhuǎn)發(fā)支持一下。

使用 AbortController 終止 Fetch 請(qǐng)求

在 fetch 之前,我們請(qǐng)求后端的資源使用的方式是通過(guò) XMLHttpRequest 這個(gè)構(gòu)造函數(shù),創(chuàng)建一個(gè) xhr 對(duì)象,然后通過(guò)這個(gè) xhr 對(duì)象進(jìn)行請(qǐng)求的發(fā)送以及接收。

const xhr = new XMLHttpRequest();
xhr.addEventListener('load', function (e) {
console.log(this.responseText);
});
xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1');
xhr.send();

這個(gè) xhr 上也存在一個(gè) abort 方法用來(lái)進(jìn)行請(qǐng)求的終止操作。但是需要注意的是,這個(gè) abort 的執(zhí)行過(guò)程是比較模糊的。 我們不清楚 abort 在什么時(shí)候可以不進(jìn)行或終止對(duì)應(yīng)的網(wǎng)絡(luò)請(qǐng)求,又或者如果在調(diào)用 abort 方法和獲取到請(qǐng)求的資源之間存在競(jìng)爭(zhēng)條件的時(shí)候會(huì)發(fā)生什么。我們可以通過(guò)簡(jiǎn)單的代碼來(lái)實(shí)踐一下:

// ... 省略掉上面的代碼
setTimeout(() => {
xhr.abort();
}, 10);

通過(guò)添加一個(gè)延時(shí),然后取消掉對(duì)應(yīng)的請(qǐng)求;在控制臺(tái)可以看到,有時(shí)請(qǐng)求已經(jīng)獲取到結(jié)果了,但是卻沒(méi)有打印出對(duì)應(yīng)的結(jié)果;有時(shí)請(qǐng)求沒(méi)有獲取到對(duì)應(yīng)的結(jié)果,但是查看對(duì)應(yīng)的網(wǎng)絡(luò)的狀態(tài)卻是成功的。所以這里面有很多的不確定性,跟我們的感覺(jué)是比較模糊的。 等到 fetch 出來(lái)的時(shí)候,大家就在討論關(guān)于如何正確,清楚地取消一個(gè) fetch 請(qǐng)求。最早的討論可以看這里 Aborting a fetch #27 ,那已經(jīng)是7年前(2015年)的事情了,可以看到當(dāng)時(shí)的討論還是比較激烈的。大家感興趣的話可以看看當(dāng)時(shí)大家都主要關(guān)注的是哪些特性。

最終,新的規(guī)范 出來(lái)了,通過(guò) AbortController 和 AbortSignal 我們可以方便,快捷,清楚地終止一個(gè) fetch 請(qǐng)求。要注意的是,這個(gè)規(guī)范是一個(gè) DOM 層面的規(guī)范,不是 JavaScript 語(yǔ)言層面的規(guī)范?,F(xiàn)在絕大多數(shù)的瀏覽器環(huán)境和新版本的 Node.js 環(huán)境也都支持這個(gè)特性了。關(guān)于 AbortController 的兼容性,大家可以參考這里 AbortController#browser_compatibility

下面文章中的代碼例子基本上都可以直接復(fù)制粘貼到控制臺(tái)運(yùn)行的,所以感興趣的同學(xué)閱讀到對(duì)應(yīng)的部分可以直接打開(kāi)瀏覽器的控制臺(tái)去運(yùn)行一下,然后看看對(duì)應(yīng)的結(jié)果。加深一下自己對(duì)相關(guān)知識(shí)點(diǎn)的記憶。

終止正在進(jìn)行中的單個(gè)請(qǐng)

我們先通過(guò)一段代碼來(lái)給大家展示一下如何實(shí)現(xiàn)這個(gè)功能

const ac = new AbortController();
const { signal } = ac;
const resourceUrl = 'https://jsonplaceholder.typicode.com/todos/1';
fetch(resourceUrl, { signal })
.then(response => response.json())
.then(json => console.log(json))
.catch(err => {
// 不同瀏覽器的返回結(jié)果不同
console.log(err);
});
// 可以立即終止請(qǐng)求,或者設(shè)置一個(gè)定時(shí)器
// ac.abort();
setTimeout(() => {
ac.abort();
}, 10);

大家感興趣的話也可以把上面的代碼復(fù)制粘貼到瀏覽器的控制臺(tái)運(yùn)行一下,上面代碼的運(yùn)行結(jié)果如下所示:

可以看到控制臺(tái)的 Console 的輸出是:DOMException: The user aborted a request.對(duì)應(yīng)的 Network 展示的是一個(gè)取消狀態(tài)的請(qǐng)求。這說(shuō)明我們剛才發(fā)送的請(qǐng)求被終止取消掉了。能夠在一些特定的情況下主動(dòng)地取消相關(guān)的請(qǐng)求對(duì)我們應(yīng)用來(lái)說(shuō)是很重要的,這能夠減少我們用戶的流量使用以及我們應(yīng)用的內(nèi)存使用。

AbortController 的深入剖析

接下來(lái)我們來(lái)講解一下上面的代碼,第一行通過(guò) AbortController 創(chuàng)建了一個(gè) AbortController 類型的實(shí)例 ac,這個(gè)實(shí)例上有一個(gè) abort 方法和一個(gè) AbortSignal 類型的 signal 實(shí)例。然后我們通過(guò) fetch 方法去請(qǐng)求一個(gè)資源路徑,傳遞給 fetch 的選項(xiàng)把 ac 的 signal 對(duì)象傳遞進(jìn)去。fetch 方法如果獲取到了資源就會(huì)把資源打印到控制臺(tái),如果網(wǎng)絡(luò)發(fā)生了問(wèn)題,就會(huì)捕獲異常,然后把異常打印到控制臺(tái)。最后,通過(guò)一個(gè) setTimeout 延時(shí),調(diào)用 ac 的 abort 方法終止 fetch 請(qǐng)求 。

fetch 的 options 選項(xiàng)允許我們傳遞一個(gè) signal 對(duì)象;**fetch 的內(nèi)部會(huì)監(jiān)測(cè)這個(gè)對(duì)象的狀態(tài),如果這個(gè)對(duì)象的狀態(tài)從未終止的狀態(tài)變?yōu)榻K止的狀態(tài)的話,并且 fetch 請(qǐng)求還在進(jìn)行中的話,fetch 請(qǐng)求就會(huì)立即失敗。其對(duì)應(yīng)的 Promise 的狀態(tài)就會(huì)變?yōu)?Rejected**。

如何改變 signal 的狀態(tài)呢?我們可以通過(guò)調(diào)用 ac 的 abort 方法去改變 signal 的狀態(tài)。一旦我們調(diào)用了 ac.abort() 那么與之關(guān)聯(lián)的 signal 的狀態(tài)會(huì)立刻從起始狀態(tài)(非終止?fàn)顟B(tài))轉(zhuǎn)變?yōu)榻K止?fàn)顟B(tài)。

我們上面只是簡(jiǎn)單地使用了 signal 對(duì)象,這個(gè)對(duì)象是 AbortSignal 類的實(shí)例,對(duì)于 AbortSignal 我們下面會(huì)做深入的講解,這里暫時(shí)只需要知道 signal 可以作為一個(gè)信號(hào)對(duì)象傳遞給 fetch 方法,可以用來(lái)終止 fetch 的繼續(xù)進(jìn)行。 另外,在不同的瀏覽器中打印的結(jié)果可能略有不同,這個(gè)跟不同瀏覽器的內(nèi)部實(shí)現(xiàn)有關(guān)系。比如在 Firefox 中的結(jié)果如下:

在 Safari 中的結(jié)果如下:

當(dāng)然如果我們沒(méi)有終止 fetch 請(qǐng)求的話,控制臺(tái)的打印將會(huì)是:

image.png

另外大家如果需要一些模擬的數(shù)據(jù)接口的話可以試試 JSONPlaceholder ,還是很方便使用的。

批量取消多個(gè) fetch 請(qǐng)求

值得注意的是,我們的 signal 對(duì)象可以同時(shí)傳遞給多個(gè)請(qǐng)求,在需要的情況下可以同時(shí)取消多個(gè)請(qǐng)求;我們來(lái)看看如何進(jìn)行這樣的操作。代碼如下所示:

const ac = new AbortController();
const { signal } = ac;
const resourcePrefix = 'https://jsonplaceholder.typicode.com/todos/';
function todoRequest (id, { signal } = {}) {
return fetch(`${resourcePrefix}${id}`, { signal })
.then(response => response.json())
.then(json => console.log(json))
.catch(e => console.log(e));
}
todoRequest(1, { signal });
todoRequest(2, { signal });
todoRequest(3, { signal });
// 同時(shí)終止多個(gè)請(qǐng)求
ac.abort();

運(yùn)行代碼后可以在控制臺(tái)看到如下結(jié)果:

如果我們需要同時(shí)對(duì)多個(gè)請(qǐng)求進(jìn)行終止操作的的話,使用上面這種方式非常簡(jiǎn)單方便。

如果我們想自定義終止請(qǐng)求的原因的話,可以直接在 abort 方法里傳遞我們想要的原因,這個(gè)參數(shù)可以是任何 JavaScript 類型的值。傳遞的終止的原因會(huì)被 signal 接收到,然后放在它的 reason 屬性中。這個(gè)我們下面會(huì)講到。

AbortController 的相關(guān)屬性和方法

image.png

詳細(xì)介紹 AbortSignal

AbortSignal 的屬性和方法

AbortSignal 接口繼承自 EventTarget ,所以 EventTarget 對(duì)應(yīng)的屬性和方法,AbortSignal 都繼承下來(lái)了。當(dāng)然還有一些自己特有的方法和屬性,我們下面會(huì)一一講解到的。需要注意的是,AbortSignal 部分屬性有兼容性問(wèn)題,具體的兼容性大家可以參考這里 AbortSignal#browser_compatibility 。

靜態(tài)方法 abort 和 timeout

這兩個(gè)方法是 AbortSignal 類上的靜態(tài)方法,用來(lái)創(chuàng)造 AbortSignal 實(shí)例。其中 abort 用來(lái)創(chuàng)造一個(gè)已經(jīng)被終止的信號(hào)對(duì)象。我們來(lái)看下面的例子:

// ... 省略 todoRequest 函數(shù)的定義
// Safari 暫時(shí)不支持, Firefox 和 Chrome 支持
// abort 可以傳遞終止的原因
const abortedAS = AbortSignal.abort();
// 再發(fā)送之前信號(hào)終止,請(qǐng)求不會(huì)被發(fā)送
todoRequest(1, { signal: abortedAS });
console.warn(abortedAS);

運(yùn)行代碼,控制臺(tái)的輸出結(jié)果如下:

image.png

對(duì)應(yīng)的請(qǐng)求甚至都沒(méi)有發(fā)送出去

image.png

我們也可以給 abort 方法傳遞終止的原因,比如是一個(gè)對(duì)象:

// ...
const abortedAS = AbortSignal.abort({
type: 'USER_ABORT_ACTION',
msg: '用戶終止了操作'
});
// ...

那么輸出的結(jié)果就如下圖所示:

image.png

signal 的 reason 屬性就變成了我們自定義的值。

同樣的,大家看到 timeout 應(yīng)該很容易想到是創(chuàng)造一個(gè)多少毫秒后會(huì)被終止的 signal 對(duì)象。代碼如下:

// ... 省略部分代碼
const timeoutAS = AbortSignal.timeout(10);
todoRequest(1, { signal: timeoutAS }).then(() => {
console.warn(timeoutAS);
});
console.log(timeoutAS);

代碼的運(yùn)行結(jié)果如下:

image.png

可以看到我們打印了兩次 timeoutAS,第一次是立即打印的,第二次是等到請(qǐng)求被終止后打印的??梢钥吹降谝淮蛴〉臅r(shí)候,timeoutAS 的狀態(tài)還是沒(méi)有被終止的狀態(tài)。當(dāng)請(qǐng)求被終止后,第二次打印的結(jié)果表明 timeoutAS 這個(gè)時(shí)候已經(jīng)被終止了,并且 reason 屬性的值表明了這次請(qǐng)求被終止是因?yàn)槌瑫r(shí)的原因。

屬性 aborted 和 reason

AbortSignal 實(shí)例有兩個(gè)屬性;一個(gè)是 aborted 表示當(dāng)前信號(hào)對(duì)象的狀態(tài)是否是終止的狀態(tài),false 是起始狀態(tài),表示信號(hào)沒(méi)有被終止,true 表示信號(hào)對(duì)象已經(jīng)被終止了。

reason 屬性可以是任何的 JavaScript 類型的值,如果我們?cè)谡{(diào)用 abort 方法的時(shí)候沒(méi)有傳遞終止信號(hào)的原因,那么就會(huì)使用默認(rèn)的原因。默認(rèn)的原因有兩種,一種是通過(guò) abort 方法終止信號(hào)對(duì)象,并且沒(méi)有傳遞終止的原因,那么這個(gè)時(shí)候 reason 的默認(rèn)值就是: DOMException: signal is aborted without reason;如果是通過(guò) timeout 方法終止信號(hào)對(duì)象,那么這個(gè)時(shí)候的默認(rèn)原因就是:DOMException: signal timed out。如果我們主動(dòng)傳遞了終止的原因,那么對(duì)應(yīng)的 reason 的值就是我們傳遞進(jìn)去的值。

實(shí)例方法 throwIfAborted

這個(gè)方法通過(guò)名稱大家也能猜出來(lái)是什么作用,那就是當(dāng)調(diào)用 throwIfAborted 的時(shí)候,如果這個(gè)時(shí)候 signal 對(duì)象的狀態(tài)是終止的,那么就會(huì)拋出一個(gè)異常,異常的值就是對(duì)應(yīng) signal 的 reason 值??梢钥聪旅娴拇a例子:

const signal = AbortSignal.abort();
signal.throwIfAborted();
// try {
// signal.throwIfAborted();
// } catch (e) {
// console.log(e);
// }

運(yùn)行后在控制臺(tái)的輸出如下:

image.png

可以看到直接拋出異常,這個(gè)時(shí)候我們可以通過(guò) try ... catch ... 進(jìn)行捕獲,然后再進(jìn)行對(duì)應(yīng)的邏輯處理。這個(gè)方法也是很有幫助的,我們?cè)诤竺鏁?huì)講到。當(dāng)我們實(shí)現(xiàn)一個(gè)自定義的可以主動(dòng)取消的 Promise 的時(shí)候這個(gè)方法就很有用。

事件監(jiān)聽(tīng) abort

對(duì)于 signal 對(duì)象來(lái)說(shuō),它還可以監(jiān)聽(tīng) abort 事件,然后我們就可以在 signal 被終止的時(shí)候做一些額外的操作。下面是事件監(jiān)聽(tīng)的簡(jiǎn)單例子:

const ac = new AbortController();
const { signal } = ac;
// 添加事件監(jiān)聽(tīng)
signal.addEventListener('abort', function (e) {
console.log('signal is aborted');
console.warn(e);
});
setTimeout(() => {
ac.abort();
}, 100);

運(yùn)行后在控制臺(tái)的輸出如下:

image.png

可以看到在 signal 被終止的時(shí)候,我們之前添加的事件監(jiān)聽(tīng)函數(shù)就開(kāi)始運(yùn)行了。其中 e 表示的是接收到的事件對(duì)象,然后這個(gè)事件對(duì)象上的 target 和 currentTarget 表示的就是對(duì)應(yīng)的 signal 對(duì)象。

實(shí)現(xiàn)一個(gè)可以主動(dòng)取消的 Promise

當(dāng)我們對(duì) AbortController 以及 AbortSignal 比較熟悉的時(shí)候,我們就可以很方便的構(gòu)造出我們自定義的可以取消的 Promise 了。下面就是一個(gè)比較簡(jiǎn)單的版本,大家可以看一下:

/**
* 自定義的可以主動(dòng)取消的 Promise
*/
function myCoolPromise ({ signal }) {
return new Promise((resolve, reject) => {
// 如果剛開(kāi)始 signal 存在并且是終止的狀態(tài)可以直接拋出異常
signal?.throwIfAborted();
// 異步的操作,這里使用 setTimeout 模擬
setTimeout(() => {
Math.random() > 0.5 ? resolve('ok') : reject(new Error('not good'));
}, 1000);
// 添加 abort 事件監(jiān)聽(tīng),一旦 signal 狀態(tài)改變就將 Promise 的狀態(tài)改變?yōu)?rejected
signal?.addEventListener('abort', () => reject(signal?.reason));
});
}
/**
* 使用自定義可取消的 Promise
*/
const ac = new AbortController();
const { signal } = ac;
myCoolPromise({ signal }).then((res) => console.log(res), err => console.warn(err));
setTimeout(() => {
ac.abort();
}, 100); // 可以更改時(shí)間看不同的結(jié)果

這次的代碼稍微多了一點(diǎn),不過(guò)相信大家還是很容易就知道上面的代碼要表示的是什么意思。

首先我們自定義了 myCoolPromise 這個(gè)函數(shù),然后函數(shù)接收一個(gè)非必傳的 signal 對(duì)象;然后立即返回一個(gè)新構(gòu)建的 Promise,這個(gè) Promise 的內(nèi)部我們添加了一些額外的處理。首先我們判斷了 signal 是否存在,如果存在就調(diào)用它的 throwIfAborted 方法。因?yàn)橛锌赡苓@個(gè)時(shí)候 signal 的狀態(tài)已經(jīng)是終止的狀態(tài)了,需要立即將 Promise 的狀態(tài)變更為 rejected 狀態(tài)。

如果此時(shí) signal 的狀態(tài)還沒(méi)有改變,那么我們可以給這個(gè) signal 添加一個(gè)事件監(jiān)聽(tīng),一旦 signal 的狀態(tài)改變,我們就需要立即去改變 Promise 的狀態(tài)。

當(dāng)我們下面的 setTimeout 的時(shí)間設(shè)置為100毫秒的時(shí)候,上面的 Promise 總是拒絕的狀態(tài),所以會(huì)看到控制臺(tái)的打印結(jié)果如下:

image.png

如果我們把這個(gè)時(shí)間修改為2000毫秒的話,那么控制臺(tái)輸出的結(jié)果可能是 ok 也可能是一個(gè) not good 的異常捕獲。

有同學(xué)看到這里可能會(huì)說(shuō),好像不需要 signal 也可以實(shí)現(xiàn)主動(dòng)取消的 Promise,我可以使用一個(gè)普通的 EventTarget 結(jié)合 CustomEvent 也可以實(shí)現(xiàn)類似的效果。當(dāng)然我們也可以這樣做,但是一般情況下我們的異步操作是包含網(wǎng)絡(luò)請(qǐng)求的,如果網(wǎng)絡(luò)請(qǐng)求使用的是 fetch 方法的話,那么就必須使用 AbortSignal 類型的實(shí)例 signal 進(jìn)行信號(hào)的傳遞;因?yàn)?fetch 方法內(nèi)部會(huì)根據(jù) signal 的狀態(tài)來(lái)判斷到底需不需要終止正在進(jìn)行的請(qǐng)求。

AbortSignal 的相關(guān)屬性和方法:

image.png

開(kāi)發(fā)中其他場(chǎng)景的使用舉例

取消事件監(jiān)聽(tīng)的一種便捷方法

一般情況下,如果我們對(duì)文檔中的某個(gè) DOM 元素添加了事件監(jiān)聽(tīng),那么當(dāng)這個(gè)元素被銷毀或者移除的時(shí)候,也需要相應(yīng)的把對(duì)應(yīng)的事件監(jiān)聽(tīng)函數(shù)移除掉,不然很容易出現(xiàn)內(nèi)存泄漏的問(wèn)題。所以一般情況下我們會(huì)按照下面的方式添加并且移除相關(guān)的事件監(jiān)聽(tīng)函數(shù)。


const evtBtn = document.querySelector('.event');
const cancelBtn = document.querySelector('.cancel');
const evtHandler = (e) => {
console.log(e);
};
evtBtn.addEventListener('click', evtHandler);
// 點(diǎn)擊 cancelBtn 移除 evtBtn 按鈕的 click 事件監(jiān)聽(tīng)
cancelBtn.addEventListener('click', function () {
evtBtn.removeEventListener('click', evtHandler);
});

這種方式是最通用的方式,但是這種方式需要我們保留對(duì)應(yīng)事件監(jiān)聽(tīng)函數(shù)的引用,比如上面的 evtHandler。一旦我們丟失了這個(gè)引用,那么后面就沒(méi)有辦法取消這個(gè)事件監(jiān)聽(tīng)了。

另外,有些應(yīng)用場(chǎng)景需要你給某個(gè)元素添加很多事件處理函數(shù),取消的時(shí)候就需要一個(gè)一個(gè)去取消,很不方便。這個(gè)時(shí)候我們的 AbortSignal 就可以派上用場(chǎng)了,我們可以使用 AbortSignal 來(lái)同時(shí)取消很多事件的事件監(jiān)聽(tīng)函數(shù)。就像我們同時(shí)取消很多個(gè) fetch 請(qǐng)求一樣。代碼如下:

// ... HTML 部分參考上面的內(nèi)容
const evtBtn = document.querySelector('.event');
const cancelBtn = document.querySelector('.cancel');
const evtHandler = (e) => console.log(e);
const mdHandler = (e) => console.log(e);
const muHandler = (e) => console.log(e);
const ac = new AbortController();
const { signal } = ac;
evtBtn.addEventListener('click', evtHandler, { signal });
evtBtn.addEventListener('mousedown', mdHandler, { signal });
evtBtn.addEventListener('mouseup', muHandler, { signal });
// 點(diǎn)擊 cancelBtn 移除 evtBtn 按鈕的 click 事件監(jiān)聽(tīng)
cancelBtn.addEventListener('click', function () {
ac.abort();
});

這樣的處理方式是不是就很方便,也非常的清楚明了。

addEventListener(type, listener, options);

addEventListener 的第三個(gè)參數(shù)可以是一個(gè) options 對(duì)象,這個(gè)對(duì)象可以讓我們傳遞一個(gè) signal 對(duì)象用來(lái)作為事件取消的信號(hào)對(duì)象。就像上面我們使用 signal 對(duì)象來(lái)取消 fetch 請(qǐng)求那樣。

image.png

從上面的兼容性來(lái)說(shuō),這個(gè)屬性的兼容性還是可以的;目前只有 Opera Android 和 Node.js 暫時(shí)還不支持,如果想要使用這個(gè)新的屬性,需要針對(duì)這兩個(gè)平臺(tái)和運(yùn)行環(huán)境做一下兼容處理就好了。

一種值得借鑒的處理復(fù)雜業(yè)務(wù)邏輯的方法

我們有時(shí)開(kāi)發(fā)中會(huì)遇到一些比較復(fù)雜的處理操作,比如你要先通過(guò)好幾個(gè)接口獲取數(shù)據(jù),然后組裝數(shù)據(jù);然后再把這些數(shù)據(jù)異步地繪制渲染到頁(yè)面上。如果用戶主動(dòng)取消了這個(gè)操作或者因?yàn)槌瑫r(shí)了,我們要主動(dòng)取消這些操作。對(duì)于這種場(chǎng)景,使用 AbortController 配合 AbortSignal 也有不錯(cuò)的效果,下面舉一個(gè)簡(jiǎn)單的例子:

// 多個(gè)串行或者并行的網(wǎng)絡(luò)請(qǐng)求
const requestUserData = (signal) => {
// TODO
};
// 異步的繪制渲染操作 里面包含了 Promise 的處理
const drawAndRenderImg = (signal) => {
// TODO
};
// 獲取服務(wù)端數(shù)據(jù)并且進(jìn)行數(shù)據(jù)的繪制和渲染
function fetchServerDataAndDrawImg ({ signal }) {
signal?.throwIfAborted();
// 多個(gè)網(wǎng)絡(luò)請(qǐng)求
requestUserData(signal);
// 組裝數(shù)據(jù),開(kāi)始繪制和渲染
drawAndRenderImg(signal);
// ... 一些其他的操作
}
const ac = new AbortController();
const { signal } = ac;
try {
fetchServerDataAndDrawImg({ signal });
} catch (e) {
console.warn(e);
}
// 用戶主動(dòng)取消或者超時(shí)取消
setTimeout(() => {
ac.abort();
}, 2000);

上面是一個(gè)簡(jiǎn)化的例子,用來(lái)表示這種復(fù)雜的操作;我們可以看到,如果用戶主動(dòng)取消或者因?yàn)槌瑫r(shí)取消操作;我們上面的代碼邏輯可以很方便的處理這種情況。也不會(huì)因?yàn)樯偬幚砹艘恍┎僮鞫鴮?dǎo)致可能發(fā)生的內(nèi)存泄漏。

一旦我們想重新開(kāi)始這個(gè)操作,我們只需要再次調(diào)用 fetchServerDataAndDrawImg 并且傳遞一個(gè)新的 signal 對(duì)象就可以了。這樣處理后,重新開(kāi)始和取消的邏輯就非常清楚了。如果大家在自己的項(xiàng)目中有類似的這種操作,不妨可以試試這種處理方法。

在 Node.js 中的使用

我們不僅可以在瀏覽器環(huán)境中使用 AbortController 和 AbortSignal,還可以在 Node.js 環(huán)境中使用這兩個(gè)功能。對(duì)于 Node.js 中的 fs.readFile,fs.writeFile,http.request,https.request 和 timers 以及新版本支持的 Fetch API 都可以使用 signal 來(lái)進(jìn)行操作的取消。下面我們來(lái)舉一個(gè)簡(jiǎn)單的例子,關(guān)于讀取文件的操作:

const fs = require('fs');
const ac = new AbortController();
const { signal } = ac;
fs.readFile('data.json', { signal, encoding: 'utf8' }, (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
});
ac.abort();

運(yùn)行代碼可以看到終端的輸出如下:

image.png

經(jīng)常使用 Node.js 進(jìn)行業(yè)務(wù)開(kāi)發(fā)的同學(xué)可以嘗試使用這個(gè)新的特性,應(yīng)該對(duì)開(kāi)發(fā)會(huì)很有幫助的。

反饋和建議

這篇文章到這里就算結(jié)束啦,不知道有多少同學(xué)堅(jiān)持讀完了這篇文章;希望讀完的同學(xué)都能夠掌握好這篇文章中講解的知識(shí)。如果這篇文章幫到了你,或者打開(kāi)了你的新世界;歡迎點(diǎn)贊轉(zhuǎn)發(fā)。


網(wǎng)站標(biāo)題:如何優(yōu)雅地中斷Promise?
當(dāng)前地址:http://m.5511xx.com/article/dpcjeei.html