コンストラクター内で非同期コードを呼び出すにはどうすればいいですか? 質問する

コンストラクター内で非同期コードを呼び出すにはどうすればいいですか? 質問する

現時点では、クラス コンストラクター関数内で使用しようとしています。これは、作業中の Electron プロジェクトのasync/awaitカスタム タグを取得できるようにするためです。e-mail

customElements.define('e-mail', class extends HTMLElement {
  async constructor() {
    super()

    let uid = this.getAttribute('data-uid')
    let message = await grabUID(uid)

    const shadowRoot = this.attachShadow({mode: 'open'})
    shadowRoot.innerHTML = `
      <div id="email">A random email message has appeared. ${message}</div>
    `
  }
})

ただし、現時点ではプロジェクトは動作せず、次のエラーが発生します。

クラスコンストラクタは非同期メソッドではない可能性があります

asyncこれを回避して、この中で/を使用できるようにする方法はありますかawait? コールバックを要求する代わりに、または.then()?

ベストアンサー1

これは絶対にうまくいきません。

キーワードはとしてマークされた関数で使用asyncできますが、その関数をプロミス ジェネレーターに変換します。したがって、 としてマークされた関数はプロミスを返します。一方、コンストラクターは構築中のオブジェクトを返します。したがって、オブジェクトとプロミスの両方を返したい状況が発生しますが、これは不可能な状況です。awaitasyncasync

async/await は、本質的には Promise の構文糖であるため、Promise を使用できる場所でのみ使用できます。コンストラクターは、Promise ではなく、構築されるオブジェクトを返す必要があるため、コンストラクター内では Promise を使用できません。

これを克服するための 2 つの設計パターンがあり、どちらも Promise が登場する前に発明されました。

  1. 関数の使用init()。これは jQuery の と少し似ています.ready()。作成したオブジェクトは、そのオブジェクト自身のinitまたはready関数内でのみ使用できます。

使用法:

    var myObj = new myClass();
    myObj.init(function() {
        // inside here you can use myObj
    });

実装:

    class myClass {
        constructor () {

        }

        init (callback) {
            // do something async and call the callback:
            callback.bind(this)();
        }
    }
  1. ビルダーを使用します。JavaScript ではあまり使用されていませんが、これはオブジェクトを非同期に構築する必要がある場合に Java でよく使用される回避策の 1 つです。もちろん、ビルダー パターンは、多数の複雑なパラメーターを必要とするオブジェクトを構築するときに使用されます。これは、非同期ビルダーのユースケースとまったく同じです。違いは、非同期ビルダーはオブジェクトを返すのではなく、そのオブジェクトの約束を返すことです。

使用法:

    myClass.build().then(function(myObj) {
        // myObj is returned by the promise, 
        // not by the constructor
        // or builder
    });

    // with async/await:

    async function foo () {
        var myObj = await myClass.build();
    }

実装:

    class myClass {
        constructor (async_param) {
            if (typeof async_param === 'undefined') {
                throw new Error('Cannot be called directly');
            }
        }

        static build () {
            return doSomeAsyncStuff()
               .then(function(async_result){
                   return new myClass(async_result);
               });
        }
    }

async/await を使用した実装:

    class myClass {
        constructor (async_param) {
            if (typeof async_param === 'undefined') {
                throw new Error('Cannot be called directly');
            }
        }

        static async build () {
            var async_result = await doSomeAsyncStuff();
            return new myClass(async_result);
        }
    }

注: 上記の例では非同期ビルダーに Promise を使用していますが、厳密に言えば必須ではありません。コールバックを受け入れるビルダーを簡単に作成できます。


静的関数内で関数を呼び出す場合の注意事項。

これは、非同期コンストラクターとはまったく関係ありませんが、キーワードがthis実際に意味するものに関係しています (メソッド名を自動解決する言語、つまりキーワードを必要としない言語から来た人にとっては、少し驚くかもしれませんthis)。

キーワードthisはインスタンス化されたオブジェクトを参照します。クラスではありません。したがって、this静的関数はどのオブジェクトにもバインドされず、クラスに直接バインドされるため、通常は静的関数内では使用できません。

つまり、次のコードになります。

class A {
    static foo () {}
}

以下のことはできません:

var a = new A();
a.foo() // NOPE!!

代わりに次のように呼び出す必要があります:

A.foo();

したがって、次のコードはエラーになります。

class A {
    static foo () {
        this.bar(); // you are calling this as static
                    // so bar is undefinned
    }
    bar () {}
}

barこれを修正するには、通常の関数または静的メソッドのいずれかを作成します。

function bar1 () {}

class A {
    static foo () {
        bar1();   // this is OK
        A.bar2(); // this is OK
    }

    static bar2 () {}
}

おすすめ記事