はじめに
こんにちは!
タイトルのケースを実装する際に若干迷ったので記事にします。
同じようなことをしたい方の参考になればと思います!
問題
以下のケースの場合を考えます。
例.NGパターン
// クラス
class SampleClass {
private asyncVar: number | undefined;
// 初期化
constructor() {
const _this = this;
setTimeout(() => {
_this.asyncVar = 1; // 非同期関数(setTimeout)内でインスタンス変数を初期化
}, 1);
}
// 出力
print() {
console.log(this.asyncVar);
}
}
// インスタンス化
const instance = new SampleClass();
// 出力
instance.print(); // undefined
やりたいこととしては、SampleClassのコンストラクタ内で初期化したasyncVar
の値(1
)を、print
関数でコンソールに出力したいとします。
が、コメントにある通りこのソースではprint
関数にて1
が出力されず、undefined
が出力されてしまいます。
これは、まず同期処理が全て実行されてから非同期の処理が実行される
というJavaScriptの仕様のためです(詳細は↓の記事が分かりやすかった)。つまり、SampleClassのインスタンス化が実行されてからprint関数が実行され、最後にコンストラクタ内のsetTimeoutで指定したコールバック関数が実行される
という流れで処理が進むため、print関数が実行される時点では変数が初期化されておらず、undefined
が表示されてしまうということですね。
ちなみに、コンストラクタ関数でasync constructor() {
のような記述はできず、処理を待つようにすることはできません。
ではどのようにasyncVar
を初期化するか、↓に記載します。
解決方法
答えは、インスタンス化を行うstaticメソッドを定義し、その中でインスタンス変数を初期化する
です。
具体的には以下のソースの通りです。
OKパターン
// クラス
class SampleClass {
private asyncVar: number | undefined;
// 初期化
+ private constructor() {}
+ // 生成
+ static async create() {
+ const instance = new SampleClass();
+ await instance._initialize();
+ return instance;
+ }
+
+ // インスタンス変数の初期化
+ private async _initialize() {
+ const _this = this;
+ await new Promise((resolve) => {
+ setTimeout(() => {
+ _this.asyncVar = 1; // 非同期関数(setTimeout)内でインスタンス変数を初期化
+ resolve(true);
+ }, 1);
+ });
+ }
// 出力
print() {
console.log(this.asyncVar);
}
}
// インスタンス化
+ const instance = await SampleClass.create();
// 出力
instance.print(); // 1
ポイントは constructor
をprivate化していること と、 create
関数内で、生成したインスタンスを返却していること です。まず、constructor
をprivate化することで通常のnewを禁止し、インスタンスを生成する場合には必ずcreate
関数を経由することを強制しています。
こうすることで、create
関数内で呼び出している_initialize
関数の中で変数の初期化を待ってから生成したインスタンスを返却させることができます(_initialize
関数の中では、awaitを使ってasyncVarの初期化を待たせています)。
上述の通り、インスタンス化はnew SampleClass()
ではなく、await SampleClass.create()
で行います。こうすることで、インスタンス変数の初期化を待つことができるので、print
関数の出力結果が1
となり想定通りに出力されることになります。
おわりに
JavaScriptは非同期とthisの扱いが鬼門ですが、ここを乗り越えたら使いこなしたと言えると思います(個人的意見)。
JISOUのメンバー募集中🔥
プログラミングコーチングJISOUではメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか?
気になる方はぜひHPからライン登録お願いします!👇