LoginSignup
2

More than 3 years have passed since last update.

TypeScript(JavaScript)のイベントハンドラで実行される非同期処理の完了を待つ

Posted at

余計な非同期処理してくれるサードパーティの関数を同期的に実行する必要があったのでメモ。

解決したい問題

フォームのsubmitの直前に処理を挿入できるようにするため、カスタムイベント beforeSubmit を発火するようにした。

window.dispatchEvent(new Event('beforeSubmit'));
$form.trigger('submit');

しかし、イベントリスナ側でサードパーティの非同期関数(つまり同期処理にできない)を呼び出す必要があった。

window.addEventListener('beforeOderSubmit', function (event: Event) {
    thirdPartyAsyncFunction(callback);
});

わざわざbeforeSubmitイベントを作っているわけなんで、submit前までにこの非同期関数の処理は終わっていて欲しい。

問題の非同期関数がコールバック関数を受け付けるなら、イベント発火側からなんとかコールバックを渡せば良いと考えたが、イベントリスナが複数登録された場合はそれらの処理が終わるたびに呼び出されてしまう。コールバックでsubmitするようにしていたら、全てのイベントリスナの処理が終わる前にsubmitされてしまう可能性がある。

解決した方法

リスナでの非同期処理は全てPromiseにラップしてイベント発火側に渡して、イベント発火側でそれらの非同期処理をPromise.all()で処理して、thenでsubmitすることで解決できた。

イベント発火側

public someMethod() {
    let asyncFunctions: Promise<void>[] = [];
    window.dispatchEvent(new CustomEvent('beforeSubmit', {
        "detail": {
            "asyncFunctions": asyncFunctions,
        }
    }));

    Promise.all(asyncFunctions).then(function () {
        ...
        $form.trigger('submit');
    });
}

リスナにPromiseオブジェクトを返してもらえるように、CustomEventでパラメータとしてPromiseオブジェクトを入れる用の配列をリスナに渡している。

リスナ側

window.addEventListener('beforeOderSubmit', function (event: CustomEvent) {
    event.detail.asyncFunctions.push(
        new Promise<void>((resolve) => {
            thirdPartyAsyncFunction(resolve);
        })
    );
});

渡してもらった配列に非同期関数の呼び出しをラップしたPromiseオブジェクトを入れている。
resolveは非同期処理が終わった時点で呼び出してやる必要がある。上記の例ではthirdPartyAsyncFunctionがコールバック関数を引数としてとると仮定している。

書いてて思ったが、リスナ側では単純に非同期関数を配列に入れて返して、イベント発火側でPromiseにラップしても良かったかも。

もっとシンプルな方法を知っている人がいたら教えて下さい。

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
2