asynchronous/等待类构造函数

目前,我试图在类构造函数中使用async/await 。 这样我可以为我正在进行的一个Electron项目获得一个自定义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> ` } }) 

但是,目前这个项目不能运行,出现以下错误:

 Class constructor may not be an async method 

有没有办法绕过这个,所以我可以在这个使用asynchronous/等待? 而不是要求callback或.then()?

这是行不通的。

async关键字允许await在标记为async的函数中使用,但也可以将该函数转换为promise生成器。 所以用async标记的函数将返回一个promise。 另一方面,构造函数返回正在构造的对象。 因此,我们有一种情况,你想要返回一个对象和一个承诺:一个不可能的情况。

你只能使用asynchronous/等待,你可以使用承诺,因为它们本质上是承诺的语法糖。 您不能在构造函数中使用promise,因为构造函数必须返回要构造的对象,而不是承诺。

有两种devise模式可以解决这个问题,都是在承诺前发明的。

  1. 使用init()函数。 这有点像jQuery的.ready() 。 您创build的对象只能在自己的initready函数中使用:

    用法:

     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)(); } } 
  2. 使用build设者。 我还没有看到这在JavaScript中使用了很多,但是当一个对象需要asynchronous构build时,这是Java中比较常见的解决方法之一。 当然,构build器模式在构build需要大量复杂参数的对象时使用。 这正是asynchronous构build器的用例。 不同之处在于asynchronous构build器不返回对象,而是返回该对象的承诺:

    用法:

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

    执行:

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

注意:尽pipe在上面的例子中我们使用了asynchronous构build器的承诺,但是他们并不是严格意义上的必需。 您可以轻松地写一个接受callback的构build器。

根据你的意见,你可能应该做任何其他HTMLElement与资产加载做:使构造函数启动一个侧加载操作,生成负载或错误事件取决于结果。

是的,这意味着使用承诺,但它也意味着“以与其他HTML元素相同的方式进行操作”,因此您的公司很好。 例如:

 var img = new Image(); img.onload = function(evt) { ... } img.addEventListener("load", evt => ... ); img.onerror = function(evt) { ... } img.addEventListener("error", evt => ... ); img.src = "some url"; 

这将启动源资产的asynchronous负载,当它成功时,以onload结束,当它出错时,以onerror结束。 所以,让你自己的class级也这样做:

 class EMailElement extends HTMLElement { constructor() { super(); this.uid = this.getAttribute('data-uid'); } setAttribute(name, value) { super.setAttribute(name, value); if (name === 'data-uid') { this.uid = value; } } set uid(input) { if (!input) return; const uid = parseInt(input); // don't fight the river, go with the flow let getEmail = new Promise( (resolve, reject) => { yourDataBase.getByUID(uid, (err, result) => { if (err) return reject(err); resolve(result); }); }); // kick off the promise, which will be async all on its own getEmail() .then(result => { this.renderLoaded(result.message); }) .catch(error => { this.renderError(error); }); } }; customElements.define('e-mail', EmailElement); 

然后你使renderLoaded / renderError函数处理事件调用和影子dom:

  renderLoaded(message) { const shadowRoot = this.attachShadow({mode: 'open'}); shadowRoot.innerHTML = ` <div class="email">A random email message has appeared. ${message}</div> `; // is there an ancient event listener? if (this.onload) { this.onload(...); } // there might be modern event listeners. dispatch an event. this.dispatchEvent(new Event('load', ...)); } renderFailed() { const shadowRoot = this.attachShadow({mode: 'open'}); shadowRoot.innerHTML = ` <div class="email">No email messages.</div> `; // is there an ancient event listener? if (this.onload) { this.onerror(...); } // there might be modern event listeners. dispatch an event. this.dispatchEvent(new Event('error', ...)); } 

另外请注意,我将你的id改为了一个class ,因为除非你写了一些奇怪的代码,只允许一个页面上的<e-mail>元素的一个实例,你不能使用一个唯一的标识符,然后把它分配给一堆的元素。

你也可以在你的构造函数中创build一个自动执行的asynchronous匿名函数:

 class MyClass { constructor() { (async () => { let result = await foo(); this.someProperty = result; console.log(this.someProperty);// outputs: bar })(); } } function foo() { return new Promise((resolve, reject) => { setTimeout(() => resolve('bar'), 2000); }); } new MyClass(); 

其他的答案是缺less明显的。 只需从构造函数中调用一个asynchronous函数:

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

你应该添加函数实例。 Promise将自动识别为Promise.resolve的可靠对象

 const asyncSymbol = Symbol(); class MyClass { constructor() { // nothing to do with constructor } then(resolve, reject) { return (this[asyncSymbol] = this[asyncSymbol] || new Promise((innerResolve, innerReject) => { setTimeout(() => innerResolve(this), 3000) })).then(resolve, reject) } } async function wait() { const myInstance = await new MyClass(); alert('run 3s later') }