Javascript 异步/等待类构造函数
目前,我正在尝试在类构造函数中使用Javascript 异步/等待类构造函数,javascript,node.js,async-await,Javascript,Node.js,Async Await,目前,我正在尝试在类构造函数中使用async/await。这样我就可以为我正在进行的一个电子项目获得一个自定义的e-mail标签 customElements.define('e-mail', class extends HTMLElement { async constructor() { super() let uid = this.getAttribute('data-uid') let message = await grabUID(uid) co
async/await
。这样我就可以为我正在进行的一个电子项目获得一个自定义的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>
`
}
})
有没有一种方法可以避免这种情况,这样我就可以在其中使用async/Wait?不需要回调或.then()?这可能永远不会起作用
async
关键字允许在标记为async
的函数中使用wait
,但它也将该函数转换为承诺生成器。因此,标记为async
的函数将返回一个承诺。另一方面,构造函数返回它正在构造的对象。因此,我们有一种情况,你想返回一个对象和一个承诺:一种不可能的情况
您只能在可以使用承诺的地方使用async/await,因为它们本质上是承诺的语法糖。不能在构造函数中使用承诺,因为构造函数必须返回要构造的对象,而不是承诺
有两种设计模式可以克服这一点,它们都是在承诺出现之前发明的
init()
函数。这有点像jQuery的.ready()
。您创建的对象只能在其自己的init
或ready
函数中使用:
用法:
var myObj = new myClass();
myObj.init(function() {
// inside here you can use myObj
});
myClass.build().then(function(myObj) {
// myObj is returned by the promise,
// not by the constructor
// or builder
});
// with async/await:
async function foo () {
var myObj = await myClass.build();
}
实施:
class myClass {
constructor () {
}
init (callback) {
// do something async and call the callback:
callback.bind(this)();
}
}
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);
});
}
}
var myObj = new myClass();
myObj.init(function() {
// inside here you can use myObj
});
myClass.build().then(function(myObj) {
// myObj is returned by the promise,
// not by the constructor
// or builder
});
// with async/await:
async function foo () {
var myObj = await myClass.build();
}
实施:
class myClass {
constructor () {
}
init (callback) {
// do something async and call the callback:
callback.bind(this)();
}
}
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);
});
}
}
使用异步/等待实现:
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);
}
}
关于在静态函数中调用函数的注意事项。 这与异步构造函数无关,但与关键字
This
的实际含义有关(对于来自自动解析方法名称的语言的人来说,这可能有点令人惊讶,即不需要This
关键字的语言)
this
关键字引用实例化的对象。不是这个班。因此,您通常不能在静态函数中使用this
,因为静态函数没有绑定到任何对象,而是直接绑定到类
也就是说,在以下代码中:
class A {
static foo () {}
}
你不能这样做:
var a = new A();
a.foo() // NOPE!!
相反,您需要将其称为:
A.foo();
因此,以下代码将导致错误:
class A {
static foo () {
this.bar(); // you are calling this as static
// so bar is undefinned
}
bar () {}
}
要解决此问题,您可以将bar
设置为常规函数或静态方法:
function bar1 () {}
class A {
static foo () {
bar1(); // this is OK
A.bar2(); // this is OK
}
static bar2 () {}
}
根据您的评论,您可能应该像其他HtmleElement在资产加载时所做的那样:让构造函数启动一个侧加载操作,根据结果生成一个加载或错误事件 是的,这意味着使用承诺,但它也意味着“以与其他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 {
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', ...));
}
renderload(消息){
const shadowRoot=this.attachShadow({mode:'open'});
shadowRoot.innerHTML=`
出现了一封随机电子邮件。${message}
`;
//有古代的事件监听器吗?
如果(此.onload){
这个.onload(…);
}
//可能存在现代事件侦听器。请发送事件。
dispatchEvent(新事件('load',…);
}
renderFailed(){
const shadowRoot=this.attachShadow({mode:'open'});
shadowRoot.innerHTML=`
没有电子邮件。
`;
//有古代的事件监听器吗?
如果(此.onload){
这是一个错误(…);
}
//可能存在现代事件侦听器。请发送事件。
dispatchEvent(新事件('error',…);
}
还要注意的是,我将您的
id
更改为类,因为除非您编写一些奇怪的代码,在页面上只允许
元素的单个实例,否则您不能使用唯一标识符,然后将其分配给一组元素。您应该向实例添加然后函数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)
}
其他答案忽略了显而易见的问题。只需从构造函数中调用异步函数:
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>
`
}
constructor(){
setContentAsync();
}
异步setContentAsync(){
让uid=this.getAttribute('data-uid'))
让消息=等待抓取uid(uid)
const shadowRoot=this.attachShadow({mode:'open'})
shadowRoot.innerHTML=`
出现了一封随机电子邮件。${message}
`
}
生成器模式的变体,使用call():
因为异步函数是承诺,所以可以在类上创建一个静态函数,该函数执行一个异步函数,该函数返回类的实例:
class Yql {
constructor () {
// Set up your class
}
static init () {
return (async function () {
let yql = new Yql()
// Do async stuff
await yql.build()
// Return instance
return yql
}())
}
async build () {
// Do stuff with await if needed
}
}
async function yql () {
// Do this instead of "new Yql()"
let yql = await Yql.init()
// Do stuff with yql instance
}
yql()
通过异步函数调用让yql=wait yql.init()
。您肯定可以这样做。基本上:
class AsyncConstructor {
constructor() {
return (async () => {
// All async code here
this.value = await asyncFunction();
return this; // when done
})();
}
}
要创建类,请使用:
let instance = await new AsyncConstructor();
不过,此解决方案有几个缺点:
super
注意:如果需要使用super
,则不能在异步回调中调用它
TypeScript注意:这会导致TypeScript出现问题,因为构造函数返回typePromise
'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 );
let cook = new cooksDAO( '12345' );
let cook = await new cooksDAO( '12345' );
let cook = new cooksDAO( '12345' );
async cook.getData();
constructor(props) {
super(props);
(async () => await this.qwe(() => console.log(props), () => console.log(props)))();
}
async qwe(q, w) {
return new Promise((rs, rj) => {
rs(q());
rj(w());
});
}
Interface A {
getData: object;
}
async function buildA0(...): Promise<A> { ... }
async function buildA1(...): Promise<A> { ... }
...
var message = (async function() { return await grabUID(uid) })()
class AsyncOnlyObject {
constructor() {
}
async init() {
this.someField = await this.calculateStuff();
}
async calculateStuff() {
return 5;
}
}
async function newAsync_AsyncOnlyObject() {
return await new AsyncOnlyObject().init();
}
newAsync_AsyncOnlyObject().then(console.log);
// output: AsyncOnlyObject {someField: 5}
async function newAsync_AsyncOnlyObject() {
return await Object.create(AsyncOnlyObject.prototype).init();
}
newAsync_AsyncOnlyObject().then(console.log);
// output: AsyncOnlyObject {someField: 5}
class AsyncObject {
constructor() {
throw new Error('classes descended from AsyncObject must be initialized as (await) TheClassName.anew(), rather than new TheClassName()');
}
static async anew(...args) {
var R = Object.create(this.prototype);
R.init(...args);
return R;
}
}
class MyObject extends AsyncObject {
async init(x, y=5) {
this.x = x;
this.y = y;
// bonus: we need not return 'this'
}
}
MyObject.anew('x').then(console.log);
// output: MyObject {x: "x", y: 5}
export class Foo {
constructor() {
return (async () => {
// await anything you want
return this; // Return the newly-created instance
})();
}
}
const foo = await new Foo();
class Foo {
private promiseReady;
constructor() {
this.promiseReady = this.init();
}
private async init() {
await someAsyncStuff();
}
public ready() {
return this.promiseReady;
}
}
let foo = new Foo();
foo.ready().then(() =>{
// It's ready
});
let foo= new Foo();
await foo.ready();