🙆

Promise の withResolvers() メソッドを知りませんでした

2024/12/29に公開

Promise の reolver 関数を外で使いたい

HTML/JavaSrcipt プログラミングの話です。

とある案件で、外部から非同期にセットされるオブジェクトを待って、そのオブジェクトに対する処理を実装する必要が発生しました。

そこで自分は以下のように実装しました。

class Foo {

  private resolve: (obj: Bar) => void;

  private promise: Promise<Bar>;

  constructor() {
    // ここで Promise を用意しつつ、resolver 関数を外に取り出す
    this.promise = new Promise<Bar>(r => this.resolve = r);
  }

  setObject(obj: Bar) {
    this.resolve(obj);
  }

  async doSomething() {
    // obj が外部からセットされるまで待機
    const obj = await this.promise();
    // obj を使って何かする
  }
}

ここで、ちょっとびみょい実装として、以下の部分があります。

// ここで Promise を用意しつつ、resolver 関数を外に取り出す
this.promise = new Promise<Bar>(r => this.resolve = r);

Promise のコンストラクタに渡すコールバック関数から、その第1引数を取り出して、それを外部の変数 (ここでは class のプロパティ) に設定して... みたいな感じで、なんだかまどろっこしいですよね。

いまは withResolvers メソッドが使えるよ

そうしたところ、とある機会に、Promise には withResolvers メソッドというものがある、と教えていただきました (出典)。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers

このメソッドを呼び出せば一発で、promise, resolver, reject の 3者が戻り値で返ってくる、というスグレモノです。

なので、上記コードは以下のように変更できます。

class Foo {

  private deferredObj: PromiseWithResolvers<Bar>;

  constructor() {
    // withResolvers メソッド一発で済む
    this.deferredObj = Promise<Bar>.withResolvers();
  }

  setObject(obj: Bar) {
    this.deferredObj.resolve(obj);
  }

  async doSomething() {
    const obj = await this.deferredObj.promise();
  }
}

コンストラクタの行数こそ違いはありませんが、コードの意図がすっきり見えてますよね。当初の実装だと、「Promise コンストラクタの引数の resolve 関数を、単にプロパティに放り込んでいるだけ? なんだこりゃ?」と一瞬迷ってしまうかもしれませんが、withResolvers はまさしく promise、resolve、reject の3者を用意するよ! ということがすぐにわかります。

プロパティ数も 2 つから 1 つに減りますし、そもそも当初実装のように1つの機能・動作を実現するのに、これら 2 つのプロパティが強力に紐付いているのも気持ちが悪いところです。

withResolvers メソッドを使うことで、ずいぶん可読性・保守性が上がりますね!

いつから使えるの?

withResolvers メソッドはやや新しめの機能です。"can I use" で検索してみると、古くは 2023年の10月頃には Chromiium 系や Firefox で、Safari はちょっと遅くて 2024年3月頃 (iOS 17.4 のリリース) から使えるようになったようです。


https://caniuse.com/?search=withResolvers

古い環境のことをあまり心配しなくてよければ、Promise.withResolvers() は上手に使っていきたいですね。

おわりに

ちゃんと毎年の The State of JS survey に目を通しておくと、モダンな JavaScript について行けますね!

https://2024.stateofjs.com/

Discussion