Heim  >  Artikel  >  Web-Frontend  >  Wie kann ich eine asynchrone Aufgabe in JavaScript abbrechen?

Wie kann ich eine asynchrone Aufgabe in JavaScript abbrechen?

青灯夜游
青灯夜游nach vorne
2020-07-03 10:24:112884Durchsuche

Wie kann ich eine asynchrone Aufgabe in JavaScript abbrechen?

Manchmal kann es schwierig sein, asynchrone Aufgaben auszuführen, insbesondere wenn eine bestimmte Programmiersprache den Abbruch von Vorgängen nicht zulässt, die versehentlich gestartet wurden oder nicht mehr benötigt werden. Glücklicherweise bietet JavaScript eine sehr praktische Funktionalität zum Abbrechen asynchroner Aktivitäten. In diesem Artikel erfahren Sie, wie Sie abbrechbare Funktionen erstellen.

Abbruchsignal

Nach der Einführung von Promise in ES2015 und dem Aufkommen einiger Web-APIs, die bald neue asynchrone Lösungen unterstützen, Es trat die Notwendigkeit auf, asynchrone Aufgaben abzubrechen. Erste Versuche konzentrieren sich darauf, eine universelle Lösung zu schaffen mit der Hoffnung, in Zukunft Teil des ECMAScript-Standards zu werden. Die Diskussion geriet jedoch schnell in eine Sackgasse und konnte das Problem nicht lösen. Daher hat die WHATWG eine eigene Lösung vorbereitet und diese direkt in das DOM AbortController in Form von eingebracht. Der offensichtliche Nachteil dieser Lösung besteht darin, dass AbortController in Node.js nicht verfügbar ist, sodass es in dieser Umgebung keine elegante oder offizielle Möglichkeit gibt, eine asynchrone Aufgabe abzubrechen.

Wie Sie in der DOM-Spezifikation sehen können, wird AbortController sehr allgemein beschrieben. Sie können es also in jeder Art von asynchroner API verwenden – auch in solchen, die noch nicht existieren. Derzeit wird nur die Fetch-API offiziell unterstützt, Sie können sie aber auch in Ihrem eigenen Code verwenden!

Bevor wir beginnen, nehmen wir uns einen Moment Zeit, um zu analysieren, wie AbortController funktioniert:

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

Schauen Sie sich den Code oben an und Sie werden es sehen Es wird festgestellt, dass zu Beginn eine neue Instanz der AbortController DOM-Schnittstelle erstellt wird (1) und deren signal-Eigenschaften an Variablen gebunden sind (2). Rufen Sie dann fetch() auf, indem Sie signal als eine seiner Optionen übergeben (3). Um das Abrufen einer Ressource abzubrechen, rufen Sie einfach abortController.abort()(4) auf. Das Versprechen von fetch() wird automatisch abgelehnt und die Kontrolle wird an den Block catch() (5) übergeben.

signal Das Anwesen selbst ist sehr interessant und der Hauptdarsteller der Show. Diese Eigenschaft ist eine Instanz der AbortSignal DOM-Schnittstelle , die über ein aborted-Attribut verfügt, das Informationen darüber enthält, ob der Benutzer die abortController.abort()-Methode aufgerufen hat. Sie können den abort-Ereignis-Listener auch an den Ereignis-Listener binden, der aufgerufen wird, wenn abortController.abort() aufgerufen wird. Mit anderen Worten: AbortController ist nur die öffentliche Schnittstelle von AbortSignal. 3bb3ba5d8a8c1e677360c9d603cb067b Daten

). Der Einfachheit halber simuliert die Beispielfunktion dies, indem sie fünf Sekunden wartet, bevor sie das Ergebnis zurückgibt:

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

calculate().then( ( result ) => {
  console.log( result );
} );

Aber manchmal möchten Benutzer diesen Strafvorgang abbrechen können. Dies ist ein teurer Vorgang. Ja, sie sollten diese Fähigkeit haben. Fügen Sie eine Schaltfläche hinzu, um die Berechnung zu starten und zu stoppen:

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

Das obige ist der detaillierte Inhalt vonWie kann ich eine asynchrone Aufgabe in JavaScript abbrechen?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen