目前,我正在尝试在类构造函数中使用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()?
您应该将该函数添加到实例中。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)
}
根据您的注释,您可能应该做其他带有资产加载的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>元素的单个实例,否则您不能使用唯一标识符,然后将其分配给一堆元素。
这里有很多伟大的知识和一些超级()深思熟虑的回答。简而言之,下面概述的技术相当简单、非递归、异步兼容,并且遵循规则。更重要的是,我认为这里还没有正确地覆盖它-尽管如果错误请纠正我!
我们不调用方法,而是简单地将II(A)FE赋值给实例prop:
// it's async-lite!
class AsyncLiteComponent {
constructor() {
// our instance includes a 'ready' property: an IIAFE promise
// that auto-runs our async needs and then resolves to the instance
// ...
// this is the primary difference to other answers, in that we defer
// from a property, not a method, and the async functionality both
// auto-runs and the promise/prop resolves to the instance
this.ready = (async () => {
// in this example we're auto-fetching something
this.msg = await AsyncLiteComponent.msg;
// we return our instance to allow nifty one-liners (see below)
return this;
})();
}
// we keep our async functionality in a static async getter
// ... technically (with some minor tweaks), we could prefetch
// or cache this response (but that isn't really our goal here)
static get msg() {
// yes I know - this example returns almost immediately (imagination people!)
return fetch('data:,Hello%20World%21').then((e) => e.text());
}
}
看起来很简单,怎么用呢?
// Ok, so you *could* instantiate it the normal, excessively boring way
const iwillnotwait = new AsyncLiteComponent();
// and defer your waiting for later
await iwillnotwait.ready
console.log(iwillnotwait.msg)
// OR OR OR you can get all async/awaity about it!
const onlywhenimready = await new AsyncLiteComponent().ready;
console.log(onlywhenimready.msg)
// ... if you're really antsy you could even "pre-wait" using the static method,
// but you'd probably want some caching / update logic in the class first
const prefetched = await AsyncLiteComponent.msg;
// ... and I haven't fully tested this but it should also be open for extension
class Extensior extends AsyncLiteComponent {
constructor() {
super();
this.ready.then(() => console.log(this.msg))
}
}
const extendedwaittime = await new Extensior().ready;
在发帖之前,我在@slebetman的评论中对这种技术的可行性进行了简短的讨论。我并没有完全被这种直接的解雇所说服,所以我认为我应该进一步讨论/推翻它。请尽你最大的努力吧。