Home >Web Front-end >JS Tutorial >How to cancel an async task in JavaScript?

How to cancel an async task in JavaScript?

青灯夜游
青灯夜游forward
2020-07-03 10:24:112960browse

How to cancel an async task in JavaScript?

Sometimes it can be difficult to perform asynchronous tasks, especially if the specific programming language does not allow the cancellation of operations that were started by mistake or are no longer needed. Fortunately JavaScript provides very convenient functionality for aborting asynchronous activities. In this article, you can learn how to create abortable functions.

Abort signal

Introducing Promise into ES2015 and some new asynchronous solutions have emerged Soon after the introduction of Web API, the need to cancel asynchronous tasks arose. Initial attempts focused on creating a universal solution that could later become part of the ECMAScript standard. However, the discussion quickly reached an impasse and failed to resolve the issue. Therefore, the WHATWG prepared its own solution and introduced it directly into the DOM in the form of an AbortController. The obvious disadvantage of this solution is that AbortController is not provided in Node.js, so there is no elegant or official way to cancel an asynchronous task in that environment. As you can see in the DOM specification,

AbortController

is described in a very general way. So you can use it in any kind of asynchronous API - even ones that don't exist yet. Currently only the Fetch API is officially supported, but you can use it in your own code too! Before we begin, let’s take a moment to analyze how

AbortController

works:

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
View above code, you'll notice that at the beginning a new instance of the

AbortController

DOM interface is created (1) and its signal property is bound to a variable (2). Then call fetch() passing signal as one of its options (3). To abort retrieving a resource, you simply call abortController.abort()(4). It will automatically reject the fetch() promise, and control will be passed to the catch() block (5).

signal

The property itself is pretty interesting and it's the main star of the show. This property is an instance of the AbortSignal DOM interface that has an aborted property containing information about whether the user has called abortController.abort()Method information. You can also bind an abort event listener to an event listener that will be called when abortController.abort() is called. In other words: AbortController is just the public interface of AbortSignal.

Terminable functionSuppose we use an asynchronous function to perform some very complex calculations (for example,

Async processing from a large array The data

). For simplicity, the example function simulates this by waiting five seconds before returning the result:

function calculate() {
  return new Promise( ( resolve, reject ) => {
    setTimeout( ()=> {
      resolve( 1 );
    }, 5000 );
  } );
}

calculate().then( ( result ) => {
  console.log( result );
} );
But sometimes users want to be able to abort this cost Expensive operation. Yes, they should have this ability. Add a button to start and stop the calculation:

<button id="calculate">Calculate</button>

<script type="module">
document.querySelector(&#39;#calculate&#39;).addEventListener(&#39;click&#39;,async({ target })=>{//1
    target.innerText = &#39;Stop calculation&#39;;

    const result = await calculate(); // 2

    alert( result ); // 3

    target.innerText = &#39;Calculate&#39;;
  } );

  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(&#39;#calculate&#39;).addEventListener(&#39;click&#39;,async ( { target } )=>{
    if ( abortController ) {
      abortController.abort(); // 5

      abortController = null;
      target.innerText = &#39;Calculate&#39;;

      return;
    }

    abortController = new AbortController(); // 3
    target.innerText = &#39;Stop calculation&#39;;

    try {
      const result = await calculate( abortController.signal ); // 4

      alert( result );
    } catch {
      alert( &#39;WHY DID YOU DO THAT?!&#39; ); // 9
    } finally { // 10
      abortController = null;
      target.innerText = &#39;Calculate&#39;;
    }
  } );

  function calculate( abortSignal ) {
    return new Promise( ( resolve, reject ) => {
      const timeout = setTimeout( ()=> {
        resolve( 1 );
      }, 5000 );

      abortSignal.addEventListener( &#39;abort&#39;, () => { // 6
        const error = new DOMException(
         &#39;Calculation aborted by the user&#39;, &#39;AbortError&#39; 
         );

        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( 
    &#39;Calculation aborted by the user&#39;, &#39;AbortError&#39; 
    ); // 1

    if ( abortSignal.aborted ) { // 2
      return reject( error );
    }

    const timeout = setTimeout( ()=> {
      resolve( 1 );
    }, 5000 );

    abortSignal.addEventListener( &#39;abort&#39;, () => {
      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视频教程

The above is the detailed content of How to cancel an async task in JavaScript?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete