때로는 비동기 작업을 수행하는 것이 어려울 수 있습니다. 특히 특정 프로그래밍 언어가 실수로 시작되었거나 더 이상 필요하지 않은 작업의 취소를 허용하지 않는 경우에는 더욱 그렇습니다. 다행히 JavaScript는 비동기 활동을 중단하는 데 매우 편리한 기능을 제공합니다. 이 문서에서는 중단 가능한 함수를 만드는 방법을 배울 수 있습니다.
Abort 신호
ES2015에 Promise
가 도입되고 새로운 비동기 솔루션을 지원하기 위해 일부 웹 API가 등장한 직후 비동기 작업을 취소해야 할 필요성이 나타났습니다Promise
引入 ES2015 并出现了一些支持新异步解决方案的 Web API 之后不久,需要取消异步任务的需求就出现了。最初的尝试集中在创建通用解决方案上,并期待以后可以成为 ECMAScript 标准的一部分。但是,讨论很快陷入僵局,无法解决问题。因此,WHATWG 准备了自己的解决方案,并以 AbortController
的形式将其直接引入 DOM。这种解决方案的明显缺点是 Node.js 中不提供 AbortController
,从而在该环境没有任何优雅或官方的方式来取消异步任务。
正如你在 DOM 规范中所看到的,AbortController
是用一种非常通用的方式描述的。所以你可以在任何类型的异步 API 中使用 —— 甚至是那些目前还不存在的 API。目前只有 Fetch API 正式支持,但是你也可以在自己的代码中使用它!
在开始之前,让我们花点时间分析一下 AbortController
. 초기 시도는 ECMAScript 표준 부분이 되기를 희망하면서 범용 솔루션을 만드는
AbortController를 사용하여 자체 솔루션을 준비했습니다.
는 이를 DOM에 직접 소개합니다. 이 솔루션의 명백한 단점은 Node.js에서 AbortController
가 제공되지 않으므로 이 환경에서 비동기 작업을 취소하는 우아하거나 공식적인 방법이 없다는 것입니다. DOM 사양에서 볼 수 있듯이 AbortController
는 매우 일반적인 방식으로 설명됩니다. 따라서 아직 존재하지 않는 API라도 모든 종류의 비동기 API에서 사용할 수 있습니다. 현재는 Fetch API만 공식적으로 지원되지만, 자신의 코드에서도 사용할 수 있습니다!
시작하기 전에 잠시 AbortController
의 작동 방식을 분석해 보겠습니다. AbortController
DOM 接口的新实例(1),并将其 signal
属性绑定到变量(2)。然后调用 fetch()
并传递 signal
作为其选项之一(3)。要中止获取资源,你只需调用abortController.abort()
(4)。它将自动拒绝 fetch()
的 promise,并且控件将传递给 catch()
块(5)。
signal
属性本身非常有趣,它是该节目的主要明星。该属性是 AbortSignal
DOM 接口的实例,该实例具有 aborted
属性,其中包含有关用户是否已调用 abortController.abort()
方法的信息。你还可以将 abort
事件侦听器绑定到将要调用 abortController.abort()
时调用的事件监听器。换句话说:AbortController
只是 AbortSignal
const abortController = new AbortController(); // 1
const abortSignal = abortController.signal; // 2
fetch( 'http://example.com', {
signal: abortSignal // 3
} ).catch( ( { message } ) => { // 5
console.log( message );
} );
abortController.abort(); // 4
위 코드를 보면 처음에 AbortController DOM 인터페이스의 새 인스턴스(1)이며 해당 <code>signal
속성을 변수(2)에 바인딩합니다. 그런 다음 fetch()
를 호출하고 signal
을 옵션 중 하나로 전달합니다(3). 리소스 검색을 중단하려면 abortController.abort()
(4)를 호출하기만 하면 됩니다. 자동으로 fetch()
약속을 거부하고 제어권이 catch()
블록(5)으로 전달됩니다.
signal
속성 자체는 꽤 흥미롭고 쇼의 주요 스타입니다. 이 속성은 AbortSignal
DOM의 인스턴스입니다. 인터페이스 , 이 인스턴스에는 사용자가 abortController.abort()
메서드를 호출했는지 여부에 대한 정보가 포함된 abort
속성이 있습니다. abortController.abort()
가 호출될 때 호출되는 이벤트 리스너에 abort
이벤트 리스너를 바인딩할 수도 있습니다. 즉, AbortController
는 AbortSignal
의 공개 인터페이스일 뿐입니다.
종료 가능 함수
매우 복잡한 계산을 수행하기 위해 비동기 함수를 사용한다고 가정해 보겠습니다(예:
대규모 배열의 데이터 처리 🎜 비동기식). 단순화를 위해 예제 함수는 결과를 반환하기 전에 5초를 기다려 이를 시뮬레이션합니다. 🎜🎜🎜🎜🎜function calculate() { return new Promise( ( resolve, reject ) => { setTimeout( ()=> { resolve( 1 ); }, 5000 ); } ); } calculate().then( ( result ) => { console.log( result ); } );🎜 그러나 때때로 사용자는 비용이 많이 드는 이 작업을 중단할 수 있기를 원합니다. 네, 이런 능력이 있어야 합니다. 계산을 시작하고 중지하는 버튼을 추가하세요: 🎜
<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>
在上面的代码中,向按钮(1)添加一个异步 click
事件侦听器,并在其中调用 calculate()
函数(2)。五秒钟后,将显示带有结果的警报对话框(3)。另外, script [type = module]
用于强制 JavaScript 代码进入严格模式——因为它比 'use strict'
编译指示更为优雅。
现在添加中止异步任务的功能:
{ // 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 } ); } ); } }
如你所见,代码变得更长了。但是没有理由惊慌,它并没有变得更难理解!
一切都包含在块(1)中,该块相当于IIFE。因此,abortController
变量(2)不会泄漏到全局作用域内。
首先,将其值设置为 null
。鼠标单击按钮时,此值会更改。然后将其值设置为 AbortController
的新实例(3)。之后,将实例的 signal
属性直接传递给你的 calculate()
函数(4)。
如果用户在五秒钟之内再次单击该按钮,则将导致调用 abortController.abort()
函数(5)。反过来,这将在你先前传递给 calculate()
的 AbortSignal
实例上触发 abort
事件(6)。
在 abort
事件侦听器内部,删除了滴答计时器(7)并拒绝了带有适当错误的promise (8; 根据规范 ,它必须是类型为 'AbortError'
的 DOMException
)。该错误最终把控制权传递给 catch
(9)和 finally
块(10)。
你还应该准备处理如下情况的代码:
const abortController = new AbortController(); abortController.abort(); calculate( abortController.signal );
在这种情况下,abort
事件将不会被触发,因为它发生在将信号传递给 calculate()
函数之前。因此你应该进行一些重构:
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 ); } ); } ); }
错误被移到顶部(1)。因此,你可以在代码不同部分中重用它(但是,创建一个错误工厂会更优雅,尽管听起来很愚蠢)。另外出现了一个保护子句,检查 abortSignal.aborted
(2)的值。如果等于 true
,那么 calculate()
函数将会拒绝带有适当错误的 promise,而无需执行任何其他操作。
这就是创建完全可中止的异步函数的方式。 演示可在这里获得(https://blog.comandeer.pl/ass...)。请享用!
英文原文地址:https://ckeditor.com/blog/Aborting-a-signal-how-to-cancel-an-asynchronous-task-in-JavaScript/
相关教程推荐:JavaScript视频教程
위 내용은 JavaScript에서 비동기 작업을 취소하는 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!