目前,我正在尝试在类构造函数中使用async/await。这样我就可以为我正在进行的Electron项目获得一个自定义的电子邮件标签。

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>
    `
  }
})

然而,目前项目不工作,有以下错误:

Class constructor may not be an async method

是否有一种方法来规避这一点,以便我可以使用异步/等待在这?而不是要求回调或.then()?


当前回答

其他答案都忽略了一个显而易见的事实。简单地从你的构造函数调用一个async函数:

constructor() {
    setContentAsync();
}

async setContentAsync() {
    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>
    `
}

其他回答

不像其他人说的,你可以让它工作。

JavaScript类可以从它们的构造函数返回任何东西,甚至是另一个类的实例。因此,您可以从解析到其实际实例的类的构造函数返回一个Promise。

下面是一个例子:

export class Foo {

    constructor() {

        return (async () => {

            // await anything you want

            return this; // Return the newly-created instance
        })();
    }
}

然后,你将这样创建Foo的实例:

const foo = await new Foo();

构建器模式的变体,使用call():

function asyncMethod(arg) {
    function innerPromise() { return new Promise((...)=> {...}) }
    innerPromise().then(result => {
        this.setStuff(result);
    }
}

const getInstance = async (arg) => {
    let instance = new Instance();
    await asyncMethod.call(instance, arg);
    return instance;
}

我根据@Downgoat的回答制作了这个测试用例。 它运行在NodeJS上。 这是Downgoat的代码,其中异步部分由setTimeout()调用提供。

'use strict';
const util = require( 'util' );

class AsyncConstructor{

  constructor( lapse ){
    this.qqq = 'QQQ';
    this.lapse = lapse;
    return ( async ( lapse ) => {
      await this.delay( lapse );
      return this;
    })( lapse );
  }

  async delay(ms) {
    return await new Promise(resolve => setTimeout(resolve, ms));
  }

}

let run = async ( millis ) => {
  // Instatiate with await, inside an async function
  let asyncConstructed = await new AsyncConstructor( millis );
  console.log( 'AsyncConstructor: ' + util.inspect( asyncConstructed ));
};

run( 777 );

我的用例是web应用程序服务器端的dao。 当我看到dao时,它们每个都关联到一个记录格式,在我的例子中是一个MongoDB集合,例如一个厨师。 cooksDAO实例保存厨师的数据。 在我不安的思绪中,我将能够实例化一个cook的DAO,并提供cookId作为参数,实例化将创建对象并使用cook的数据填充它。 因此需要在构造函数中运行异步的东西。 我想写:

let cook = new cooksDAO( '12345' );  

来拥有可用的属性,如cook.getDisplayName()。 有了这个解决方案,我必须做:

let cook = await new cooksDAO( '12345' );  

这和理想情况非常相似。 此外,我需要在一个异步函数中做到这一点。

我的b计划是把数据加载放在构造函数之外,基于@slebetman的建议使用init函数,并像这样做:

let cook = new cooksDAO( '12345' );  
async cook.getData();

这并不违反规则。

其他答案都忽略了一个显而易见的事实。简单地从你的构造函数调用一个async函数:

constructor() {
    setContentAsync();
}

async setContentAsync() {
    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>
    `
}

我发现自己遇到了这样的情况,最终使用了IIFE

// using TypeScript

class SomeClass {
    constructor() {
        // do something here
    }

    doSomethingAsync(): SomeClass {
        (async () => await asyncTask())();
        return this;
    }
}

const someClass = new SomeClass().doSomethingAsync();

如果你有其他依赖于异步任务的任务,你可以在IIFE完成执行后运行它们。