ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript で非同期タスクをキャンセルするにはどうすればよいですか?

JavaScript で非同期タスクをキャンセルするにはどうすればよいですか?

青灯夜游
青灯夜游転載
2020-07-03 10:24:112946ブラウズ

JavaScript で非同期タスクをキャンセルするにはどうすればよいですか?

場合によっては、特に特定のプログラミング言語で誤って開始された操作や不要になった操作のキャンセルが許可されていない場合、非同期タスクの実行が困難になることがあります。幸いなことに、JavaScript は非同期アクティビティを中止するための非常に便利な機能を提供します。この記事では、中止可能な関数を作成する方法を学びます。

アボートシグナル

ES2015 への Promise の導入といくつかの新しい非同期ソリューションが登場 Web API の導入直後, 非同期タスクをキャンセルする必要が生じました。初期の試みは、後に ECMAScript 標準の一部となる可能性のある ユニバーサル ソリューションの作成 に焦点を当てていました。しかし、議論はすぐに行き詰まり、問題は解決できなかった。したがって、WHATWG は独自のソリューションを準備し、AbortController の形式で DOM に直接導入しました。このソリューションの明らかな欠点は、AbortController が Node.js で提供されていないため、その環境で非同期タスクをキャンセルするエレガントなまたは公式な方法がないことです。 DOM 仕様でわかるように、

AbortController

は非常に一般的な方法で記述されています。そのため、まだ存在しない非同期 API であっても、あらゆる種類の非同期 API で使用できます。現在、Fetch API のみが正式にサポートされていますが、独自のコードでも使用できます。 始める前に、

AbortController

がどのように機能するかを分析してみましょう:

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)、その signal プロパティが変数にバインドされていることがわかります (2)。次に、オプションの 1 つとして signal を渡して fetch() を呼び出します (3)。リソースの取得を中止するには、abortController.abort()(4) を呼び出すだけです。 fetch() プロミスは自動的に拒否され、制御は catch() ブロック (5) に渡されます。

signal

物件自体は非常に興味深いもので、ショーの主役です。このプロパティは、ユーザーが abortController.abort()# を呼び出したかどうかに関する情報を含む aborted プロパティを持つ AbortSignal DOM インターフェイス のインスタンスです。 # #メソッド情報。 abort イベント リスナーを、abortController.abort() が呼び出されたときに呼び出されるイベント リスナーにバインドすることもできます。言い換えると、AbortController は、AbortSignal のパブリック インターフェイスにすぎません。

終了可能な関数

非同期関数を使用して非常に複雑な計算 (たとえば、大規模な関数からの 非同期処理) を実行するとします。配列 データ

)。わかりやすくするために、関数例では、結果を返す前に 5 秒待機することでこれをシミュレートします。はい、彼らはこの能力を持っているはずです。計算を開始および停止するボタンを追加します:

<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视频教程

以上がJavaScript で非同期タスクをキャンセルするにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。