ゆっくりのんびり。

いの (@inox_ee) です

【JavaScript】async constructor がしたい

はじめに

卒論で書いたコード(JavaScript SPA)があまりにも汚かったので、最近オブジェクト指向 & モダンなJSを使って書き直してます。 そんな中での一幕。

async constructor() ができない

例えば以下のようなクラスを書いたとします。

import * as THREE from "THREE"

class Hoge {
  constructor() {
    this.scene = new THREE.Scene();
  }

  addCamera() {
    this.scene.add(new THREE.Camera());
  }
}

function main() {
  const hoge = new Hoge();
  hoge.addCamera();
}

一見大丈夫そうですが、これでは Uncaught TypeError: Cannot read property 'add' of undefined と言われてしまいます。
addCamera() が非同期に実行されたため THREE.Sceneインスタンス生成が終わらぬ間に呼び出されたと推測できます。

それなら、と以下のコードを書いてみる。

class Hoge {
  async constructor() {
    this.scene = await Promise.resolve(new THREE.Scene());
  }
  // 以下略
}

これも残念ながら Uncaught SyntaxError: Class constructor may not be an async method と言われてしまいます。コンストラクタにはasyncがつけられない模様。

現状の対応

とりあえずこんな感じで書いてる。

class Hoge {
  constructor() {
    this.scene = null;  // 当然だが無くても動く。プロパティを明示するために書いたが、TypeScript使えやと言われそう。
  }

  static async build() {
    const hoge = new Hoge();
    hoge.scene = await Promise.resolve(new THREE.Scene())
    return hoge;
  }

  addCamera() {
    this.scene.add(new THREE.Camera());
  }
}

async function main() {
  const hoge = await Hoge.build()
  hoge.addCamera()
}

自分の思いつく範囲では、「インスタンスをPromiseで返す」か「コンストラクタ単体でのインスタンス生成を諦める」かの2択になる。前者はさすがに気持ち悪い(instance.then(...) は違和感しかないだろ…)ので、後者を選択。コンストラクタ以外ならクラスメソッドもインスタンスメソッドでもasync構文が使えるようなので、 new THREE.Scene() を待ってプロパティに格納する。

私はJSょゎょゎなので、これ以外の方法があればご教授ください。

あとがき

最近オススメのVTuberは、断然にじさんじ所属の葉加瀬冬雪(通称 はかちぇ)。先日遂にメンバーシップに加入してしまった...。
ゲームがそれほど上手いわけじゃないけど、「指示厨殺し」だけど いつも楽しそうに配信している姿は元気もらえます。

youtu.be

実は歌も上手いんですよねぇ…。この空奏列車のカバーを初めて聞いたときは本当に心が震えました。今日公開のオリ曲にも期待。