av一区二区在线观看_亚洲男人的天堂网站_日韩亚洲视频_在线成人免费_欧美日韩精品免费观看视频_久草视

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

如何優(yōu)雅地取消 JavaScript 異步任務(wù)

瀏覽:84日期:2023-11-07 09:57:37

在程序中處理異步任務(wù)通常比較麻煩,尤其是那些不支持取消異步任務(wù)的編程語(yǔ)言。所幸的是,JavaScript 提供了一種非常方便的機(jī)制來(lái)取消異步任務(wù)。

中斷信號(hào)

自從 ES2015 引入了 Promise ,開(kāi)發(fā)者有了取消異步任務(wù)的需求,隨后推出的一些 Web API 也開(kāi)始支持異步方案,比如 Fetch API。TC39 委員會(huì)(就是制定 ECMAScript 標(biāo)準(zhǔn)的組織)最初嘗試定義一套通用的解決方案,以便后續(xù)作為 ECMAScript 標(biāo)準(zhǔn)。但是后來(lái)討論不出什么結(jié)果來(lái),這個(gè)問(wèn)題也就擱置了。鑒于此,WHATWG (HTML 標(biāo)準(zhǔn)制定組織)另起爐灶,自己搞出一套解決方案,直接在 DOM 標(biāo)準(zhǔn)上引入了 AbortController。這種做法的壞處顯而易見(jiàn),因?yàn)樗皇钦Z(yǔ)言層面的 ECMAScript 標(biāo)準(zhǔn),因此 Node.js 平臺(tái)也就不支持 AbortController 。

在 DOM 規(guī)范里, AbortController 設(shè)計(jì)得非常通用,因此事實(shí)上你可以用在任何異步 API 中。目前只得到 Fetch API 的官方支持,但你完全可以用在自己的異步代碼里。

在開(kāi)始介紹之前,我們先看下 AbortController 的工作原理:

const abortController = new AbortController(); // 1const abortSignal = abortController.signal; // 2fetch( ’http://kaysonli.com’, { signal: abortSignal // 3} ).catch( ( { message } ) => { // 5 console.log( message );} );abortController.abort(); // 4

上面的代碼很簡(jiǎn)單,首先創(chuàng)建了AbortController的一個(gè)實(shí)例(1),并將它的 signal 屬性賦值給一個(gè)變量(2)。然后調(diào)用fetch()并傳入 signal 參數(shù)(3)。取消請(qǐng)求時(shí)調(diào)用 abortController.abort()(4)。這樣就會(huì)自動(dòng)執(zhí)行fetch() 的 reject ,也就是進(jìn)入catch()部分(5)。

它的signal屬性是核心所在。該屬性是 AbortSignal DOM 接口的實(shí)例,它有一個(gè) aborted屬性,帶有是否調(diào)用了 abortController.abort()的相關(guān)信息。還可以在上面監(jiān)聽(tīng)abort事件,該事件在abortController.abort()調(diào)用時(shí)觸發(fā)。簡(jiǎn)單來(lái)說(shuō),AbortController 就是AbortSignal的一個(gè)公開(kāi)接口。

可取消的函數(shù)

假設(shè)有一個(gè)執(zhí)行復(fù)雜計(jì)算的異步函數(shù),為簡(jiǎn)單起見(jiàn),我們就用定時(shí)器模擬:

function calculate() { return new Promise( ( resolve, reject ) => { setTimeout( ()=> { resolve( 1 ); }, 5000 ); } );}calculate().then( ( result ) => { console.log( result );} );

可能的情況是,用戶想取消這種耗時(shí)的任務(wù)。我們用一個(gè)按鈕來(lái)開(kāi)始和停止:

<button id='calculate'>Calculate</button><script type='module'> document.querySelector( ’#calculate’ ).addEventListener( ’click’, async ( { target } ) => { // 1 target.innerText = ’Stop calculation’; const result = await calculate(); // 2 alert( result ); // 3 target.innerText = ’Calculate’; } ); function calculate() { return new Promise( ( resolve, reject ) => { setTimeout( ()=> { resolve( 1 ); }, 5000 ); } ); }</script>

上面的代碼給按鈕綁定了一個(gè)異步的 click 事件處理器(1),并在里面調(diào)用了 calculate() 函數(shù)(2)。5 秒后會(huì)彈出對(duì)話框顯示結(jié)果(3)。順便提一下,script[type=module]可以讓 JavaScript 代碼進(jìn)入嚴(yán)格模式,跟 ’use strict’ 的效果一樣。

增加中斷異步任務(wù)的功能:

{ // 1 let abortController = null; // 2 document.querySelector( ’#calculate’ ).addEventListener( ’click’, async ( { target } ) => { if ( abortController ) { abortController.abort(); // 5 abortController = null; target.innerText = ’Calculate’; return; } abortController = new AbortController(); // 3 target.innerText = ’Stop calculation’; try { const result = await calculate( abortController.signal ); // 4 alert( result ); } catch { alert( ’WHY DID YOU DO THAT?!’ ); // 9 } finally { // 10 abortController = null; target.innerText = ’Calculate’; } } ); function calculate( abortSignal ) { return new Promise( ( resolve, reject ) => { const timeout = setTimeout( ()=> { resolve( 1 ); }, 5000 ); abortSignal.addEventListener( ’abort’, () => { // 6 const error = new DOMException( ’Calculation aborted by the user’, ’AbortError’ ); clearTimeout( timeout ); // 7 reject( error ); // 8 } ); } ); }}

代碼變長(zhǎng)了很多,但是別慌,理解起來(lái)也不是很難。

最外層的代碼塊(1)相當(dāng)于一個(gè) IIFE(立即執(zhí)行的函數(shù)表達(dá)式),這樣變量 abortController(2)就不會(huì)污染全局了。

首先把它的值設(shè)為null,并且它的值隨著按鈕點(diǎn)擊而改變。隨后給它賦值為AbortController的一個(gè)實(shí)例(3),再把實(shí)例的signal屬性直接傳給 calculate()函數(shù)(4)。

如果用戶在 5 秒之內(nèi)再次點(diǎn)擊按鈕,就會(huì)執(zhí)行abortController.abort()函數(shù)(5)。這樣就會(huì)在剛才傳給 calculate()的AbortSignal實(shí)例上觸發(fā) abort 事件(6)。

在 abort 事件處理器里面清除定時(shí)器(7),然后用一個(gè)適當(dāng)?shù)漠惓?duì)象拒絕 Promise(8)。

根據(jù) DOM 規(guī)范,這個(gè)異常對(duì)象必須是一個(gè)’AbortError’ 類型的DOMException。

這個(gè)異常對(duì)象最終傳給了catch (9) 和finally (10)。

但是還要考慮這樣一種情況:

const abortController = new AbortController();abortController.abort();calculate( abortController.signal );

這種情況下 abort 事件不會(huì)觸發(fā),因?yàn)樗趕ignal傳給calculate() 函數(shù)前就執(zhí)行了。為此我們需要改造下代碼:

function calculate( abortSignal ) { return new Promise( ( resolve, reject ) => { const error = new DOMException( ’Calculation aborted by the user’, ’AbortError’ ); // 1 if ( abortSignal.aborted ) { // 2 return reject( error ); } const timeout = setTimeout( ()=> { resolve( 1 ); }, 5000 ); abortSignal.addEventListener( ’abort’, () => { clearTimeout( timeout ); reject( error ); } ); } );}

異常對(duì)象的定義移到了頂部(1),這樣就可以在兩個(gè)地方重用了。另外,多了個(gè)條件判斷abortSignal.aborted(2)。如果它的值是true,calculate()函數(shù)應(yīng)該立即拒絕 Promise,沒(méi)必要再往下執(zhí)行了。到這里我們就實(shí)現(xiàn)了一個(gè)完整的可取消的異步函數(shù),以后碰到需要處理異步任務(wù)的地方就可以派上用場(chǎng)了。

到此這篇關(guān)于如何優(yōu)雅地取消 JavaScript 異步任務(wù)的文章就介紹到這了,更多相關(guān)JavaScript 取消異步任務(wù)內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: JavaScript
相關(guān)文章:
主站蜘蛛池模板: 一区二区中文 | 99精品欧美一区二区三区综合在线 | 欧美成人免费在线视频 | 欧美一区二区三区在线播放 | 久久精品亚洲国产 | 91精品国产综合久久香蕉922 | 国产精品一区二 | 久久精品国产v日韩v亚洲 | 日韩精品一区二区三区视频播放 | 久草资源 | 精品视频一区二区 | 黄色大片免费网站 | 国产精品一区二区三 | 亚洲高清视频一区二区 | 91精品国产色综合久久 | 欧美日韩亚洲国产 | 欧美xxxx性 | 免费永久av | av第一页 | 尤物在线精品视频 | 国产一区二区三区 | 欧美精品1区2区 | 亚洲资源站 | 久久精品亚洲 | 亚洲欧美日韩电影 | 国产乱码高清区二区三区在线 | 国产一级片免费看 | 欧美日韩国产一区二区三区 | 精品一区二区久久久久久久网站 | 国产精品久久国产精品99 | 国产日韩精品久久 | 四虎影院在线播放 | 色约约视频 | 久久久久精 | 欧美日韩中文字幕在线 | 在线一区视频 | 中文字幕黄色大片 | 日韩一区二区三区视频在线观看 | 日韩av免费在线观看 | 欧美亚洲综合久久 | 久久成人久久 |