LoginSignup
49
49

More than 5 years have passed since last update.

ES2016 async/await キャンセル可能な非同期関数を実装する方法

Last updated at Posted at 2016-11-13

stack overflow にいいやり方が載っていたので紹介します。
出典:Abort ecmascript7 async function

デモを書いてみました

まずはこちらを見てください。何がやりたいかがわかりやすくなっているかと思います。
jsfiddle で動きを見る:CancellationToken

async/await にはキャンセル処理を実装するための仕組みがありませんが、 async/await の元ネタである .NET framework に CancellationToken 構造体 というものがあります。

これを JS に持ち込むことで、キャンセル可能な async 関数を実装することができます。

CancellationToken クラス

CancellationToken(ES2016実装)
class CancellationToken {

  isCancellationRequested = false;

  constructor(parentToken = null) {
    this.cancellationPromise = new Promise(resolve => {
      this.cancel = e => {
        this.isCancellationRequested = true;
        if (e) {
          resolve(e);
        } else {
          var err = new Error("cancelled");
          err.cancelled = true;
          resolve(err);
        }
      }
    });
    if (parentToken && parentToken instanceof CancellationToken) {
      parentToken.register(this.cancel);
    }
  }

  register(callback) {
    this.cancellationPromise.then(callback);
  }

  createDependentToken() {
    return new CancellationToken(this);
  }

}

使い方

キャンセルできるようにしたい async 関数の引数に CancellationToken を受け取るようにします。

// キャンセル可能な非同期関数
function delayAsync(duration, cancellationToken = null) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, duration);
    if (cancellationToken) {
      cancellationToken.register(reject);
    }
  });
}

この関数に渡したトークンを使ってキャンセルすることができます。

// トークンを新しく作る
var ct = new CancellationToken();
// 1秒後にキャンセルされるようにする
delayAsync(1000)
    .then(ct.cancel);
// 2秒かかる処理をする
delayAsync(2000, ct)
    .then(() => console.log("ok"))
    .catch(e => console.log(e.cancelled ? "cancelled" : "some other err"));

async/await スタイルでも同じようにキャンセルできます。

async function Go(cancellationToken)
{
  try{
    await delayAsync(2000, cancellationToken)
    console.log("ok")
  }catch(e){
    console.log(e.cancelled ? "cancelled" : "some other err")
  }
}
var ct = new CancellationToken();
delayAsync(1000).then(ct.cancel);
Go(ct)
49
49
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49
49