導入:なぜPromise.withResolversが必要なのか
フロントエンド開発において、Promiseの外部から処理を制御したい場面は頻繁に発生します。例えば、「ダイアログの閉じるボタンが押されるまで処理を待機させる」といったケースです。従来はPromiseコンストラクタ内でresolveやrejectを取り出す必要があり、ネストが深くなったり変数のスコープ管理が複雑になったりしていました。
Promise.withResolvers() は、これらを劇的に簡潔にする新しいAPIです。非同期処理の制御フローをより直感的に記述できるようになり、コードの可読性と保守性を向上させます。
基礎知識:Promise.withResolversとは何か
Promise.withResolvers() は、新しいPromiseオブジェクトとそのresolve関数、reject関数をセットで返す静的メソッドです。
通常、Promiseを外部から解決するには、コンストラクタ内で引数を受け取るという一段階踏む必要がありました。しかし、このメソッドを使えば、Promise本体と、それを操作するための関数を分割して直接取得できます。これにより、非同期処理の「発行」と「解決」を分離した設計が可能になります。
実装:Promise.withResolversによる解決策
実装の基本は極めてシンプルです。まずメソッドを呼び出し、返されたオブジェクトからresolveとrejectを取り出します。あとは、任意のタイミング(イベントリスナーのコールバックなど)でそれらを呼び出すだけです。Promiseのコンストラクタ内でスコープを閉じる必要がないため、外部の関数からも柔軟にPromiseを制御できます。
サンプルプログラム:ダイアログ待機の実装例
以下のコードは、ボタンクリックを待機してPromiseを解決する実用的な例です。
// 外部から制御可能なPromiseを作成
const { promise, resolve, reject } = Promise.withResolvers();
// ボタンクリック時に解決されるPromiseを待機する関数
async function waitForUserAction(buttonElement) {
console.log("操作を待機中...");
// クリックイベントでPromiseを解決
buttonElement.addEventListener("click", () => {
resolve("ユーザーが操作を完了しました");
}, { once: true });
// 待機が完了するまで処理を一時停止
const result = await promise;
console.log(result);
}
// 使用例
const btn = document.querySelector("#myButton");
waitForUserAction(btn);
応用・注意点:現場で陥りやすいバグの回避
1. 複数回呼び出しの制御
Promiseのresolveは一度しか実行されません。もしイベントが複数回発生する可能性がある場合、同じPromiseインスタンスを使い回すと意図しない挙動になります。必要に応じてPromiseを再生成するか、一度の解決でイベントリスナーを解除する({ once: true }の利用)などの工夫が必要です。
2. エラーハンドリング
reject関数を呼び出さないまま放置すると、未処理のPromise拒否が発生します。try-catch構文で囲むのはもちろんですが、タイムアウト処理を別途実装し、一定時間経過後にrejectを呼び出すような設計にすると、アプリケーションの堅牢性が高まります。
3. ブラウザ対応状況
比較的新しいAPIのため、IEや古いブラウザでは動作しません。必要に応じてポリフィルを用意するか、Babelなどのトランスパイラで変換して対応してください。モダンブラウザであれば標準でサポートされています。