目前,我正在尝试在类构造函数中使用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()?


当前回答

您可以立即调用一个返回消息的匿名异步函数,并将其设置为message变量。如果您不熟悉这种模式,您可能想看看立即调用的函数表达式(IEFES)。这将非常有效。

var message = (async function() { return await grabUID(uid) })()

其他回答

构建器模式的变体,使用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;
}

其他答案都忽略了一个显而易见的事实。简单地从你的构造函数调用一个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>
    `
}

这是可以做到的。 一个简单的代码:

class test
{
   constructor ()
   {
      return new Promise ( (resolve, reject) => { resolve(this); });
   }
   doHello() {console.log("hello");}
}
async function main()
{
   let t = await new test(); //invoking synchronously
   t.doHello(); //t is not a pormise
}
main();

或与上面相同,但添加了实际延迟,使用setTimeout

class test
{
   constructor ()
   {
      return new Promise ( (resolve, reject) =>
      {
         setTimeout (resolve, 5, this);
      });
   }
   doHello() {console.log("hello");}
}
async function main()
{  
   let t = new test(); //now t is a promise
   t.then((a)=>{ a.doHello();}); //a is the real reference to test instance
   console.log("testing"); //"testing" will be printed 5 seconds before "hello"
}
main();

这里是我在现实生活中的一段代码,使用异步图像加载:

class HeightMap extends GlVAObject
{
   #vertices = [];
   constructor (src, crossOrigin = "")
   {
      //super(theContextSetup);
      let image = new Image();
      image.src = src;
      image.crossOrigin = crossOrigin;
      return new Promise ( (resolve, reject) =>
         {
            image.addEventListener('load',  () =>
            {
               //reading pixel values from image into this.#vertices
               //and generate a heights map
               //...
               resolve(this);
            } );
         });
   }
///...
}
async function main()
{
   let vao = await new HeightMap ("./heightmaps/ArisonaCraterHeightMap.png");
///...
}
main();

@slebetmen的公认答案很好地解释了为什么这行不通。除了答案中给出的两种模式之外,另一种选择是仅通过自定义异步getter访问您的异步属性。然后构造函数()可以触发属性的异步创建,但是getter在使用或返回属性之前检查属性是否可用。

当你想在启动后初始化一个全局对象,并且你想在一个模块中做这件事时,这种方法特别有用。而不是在index.js中初始化并将实例传递到需要它的地方,只需在需要全局对象的地方调用模块。

使用

const instance = new MyClass();
const prop = await instance.getMyProperty();

实现

class MyClass {
  constructor() {
    this.myProperty = null;
    this.myPropertyPromise = this.downloadAsyncStuff();
  }
  async downloadAsyncStuff() {
    // await yourAsyncCall();
    this.myProperty = 'async property'; // this would instead by your async call
    return this.myProperty;
  }
  getMyProperty() {
    if (this.myProperty) {
      return this.myProperty;
    } else {
      return this.myPropertyPromise;
    }
  }
}

您应该将该函数添加到实例中。Promise将它识别为Promise的可支持对象。解决自动

const asyncSymbol = Symbol();
class MyClass {
    constructor() {
        this.asyncData = null
    }
    then(resolve, reject) {
        return (this[asyncSymbol] = this[asyncSymbol] || new Promise((innerResolve, innerReject) => {
            this.asyncData = { a: 1 }
            setTimeout(() => innerResolve(this.asyncData), 3000)
        })).then(resolve, reject)
    }
}

async function wait() {
    const asyncData = await new MyClass();
    alert('run 3s later')
    alert(asyncData.a)
}