目前,我正在尝试在类构造函数中使用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()?
这是可以做到的。
一个简单的代码:
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();
我根据@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();
这并不违反规则。
根据您的注释,您可能应该做其他带有资产加载的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";
这将启动源资产的异步加载,当它成功时,以onload结束,当它出错时,以onerror结束。所以,让你自己的类也这样做:
class EMailElement extends HTMLElement {
connectedCallback() {
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, use a promise:
new Promise((resolve, reject) => {
yourDataBase.getByUID(uid, (err, result) => {
if (err) return reject(err);
resolve(result);
});
})
.then(result => {
this.renderLoaded(result.message);
})
.catch(error => {
this.renderError(error);
});
}
};
customElements.define('e-mail', EmailElement);
然后你让renderLoaded/renderError函数处理事件调用和shadow 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更改为一个类,因为除非您编写一些奇怪的代码,只允许页面上的<e-mail>元素的单个实例,否则您不能使用唯一标识符,然后将其分配给一堆元素。