余計な非同期処理してくれるサードパーティの関数を同期的に実行する必要があったのでメモ。
解決したい問題
フォームの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
にラップしても良かったかも。
もっとシンプルな方法を知っている人がいたら教えて下さい。