Async / Await 클래스 생성자
현재 async/await
클래스 생성자 함수 내 에서 사용하려고 합니다. 이것은 e-mail
내가 작업중인 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
이 안에서 async / await를 사용할 수 있도록 이것을 우회하는 방법이 있습니까? 콜백이나 .then () 대신?
이것은 결코 작동 하지 않습니다 .
async
키워드 허용 await
으로 표시하는 기능에 사용될 async
뿐만 아니라 약속 발생기로 해당 함수를 변환한다. 따라서로 표시된 함수 async
는 promise를 반환합니다. 반면에 생성자는 생성중인 객체를 반환합니다. 따라서 우리는 객체와 약속을 모두 반환하려는 상황이 있습니다. 불가능한 상황입니다.
약속에 대한 구문 설탕이기 때문에 약속을 사용할 수있는 경우에만 async / await를 사용할 수 있습니다. 생성자는 promise가 아니라 생성 할 객체를 반환해야하므로 생성자에서 promise를 사용할 수 없습니다.
이를 극복하기위한 두 가지 디자인 패턴이 있으며, 둘 다 약속이 있기 전에 발명되었습니다.
의 사용
init()
기능. 이것은 jQuery의.ready()
. 생성 한 객체는 자체init
또는ready
기능 내에서만 사용할 수 있습니다 .용법:
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)(); } }
빌더를 사용하십시오. 나는 이것이 자바 스크립트에서 많이 사용되는 것을 보지 못했지만 이것은 객체가 비동기 적으로 생성되어야 할 때 자바에서 더 일반적인 해결 방법 중 하나입니다. 물론 빌더 패턴은 많은 복잡한 매개 변수가 필요한 객체를 구성 할 때 사용됩니다. 이것이 바로 비동기 빌더의 사용 사례입니다. 차이점은 비동기 빌더가 객체를 반환하지 않고 해당 객체의 약속을 반환한다는 것입니다.
용법:
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 (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); } }
참고 : 위의 예에서는 비동기 빌더에 대한 promise를 사용하지만 엄격히 말하면 필요하지 않습니다. 콜백을 수락하는 빌더를 쉽게 작성할 수 있습니다.
정적 함수 내부의 함수 호출에 대한 참고 사항.
이것은 비동기 생성자와 아무런 관련이 없지만 키워드가 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 () {}
}
확실히 할 수 있습니다 . 원래:
class AsyncConstructor {
constructor() {
return (async () => {
// All async code here
this.value = await asyncFunction();
return this; // when done
})();
}
}
클래스 사용을 만들려면 :
let instance = await new AsyncConstructor();
참고 : super를 사용해야하는 경우 비동기 콜백 내에서 호출 할 수 없습니다. 이 솔루션은 그래서 밖에서의 호출 할 필요가 없는 100 % 완벽하지만 내 생각에 그것은 매우 관용적 만 내 코드에서 모든 시간을 사용합니다.
귀하의 의견에 따라 애셋로드가있는 다른 모든 HTMLElement가 수행하는 작업을 수행해야합니다. 생성자가 사이드로드 작업을 시작하고 결과에 따라로드 또는 오류 이벤트를 생성하도록합니다.
예, 이는 promise를 사용하는 것을 의미하지만 "다른 모든 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 함수가 이벤트 호출과 섀도우 돔을 처리하도록합니다.
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
A와 class
당신이 몇 가지 이상한 코드를 작성하지 않는 한 오직 당신의 단일 인스턴스 수 있기 때문에, <e-mail>
한 페이지에 요소가 고유 한 식별자를 사용하고 요소의 무리에 할당 할 수 없습니다.
비동기 함수는 약속이므로 클래스의 인스턴스를 반환하는 비동기 함수를 실행하는 정적 함수를 클래스에 만들 수 있습니다.
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()
let yql = await Yql.init()
비동기 함수에서로 호출 합니다.
@Downgoat의 답변을 기반으로이 테스트 케이스를 만들었습니다.
NodeJS에서 실행됩니다. 이것은 setTimeout()
호출에 의해 비동기 부분이 제공되는 Downgoat의 코드 입니다.
'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 );
내 사용 사례는 웹 애플리케이션의 서버 측에 대한 DAO입니다.
내가 DAO를 볼 때, 그것들은 각각 레코드 형식, 내 경우에는 요리사와 같은 MongoDB 컬렉션과 연결되어 있습니다.
cooksDAO 인스턴스는 요리사의 데이터를 보유합니다.
불안한 마음으로 cookId를 인수로 제공하는 요리사의 DAO를 인스턴스화 할 수 있으며 인스턴스화는 개체를 만들고 요리사의 데이터로 채울 것입니다.
따라서 생성자에 비동기 작업을 실행할 필요가 있습니다.
나는 쓰고 싶었다 :
let cook = new cooksDAO( '12345' );
같이 사용할 수있는 속성을 가지고 cook.getDisplayName()
.
이 솔루션을 사용하여 다음을 수행해야합니다.
let cook = await new cooksDAO( '12345' );
이상과 매우 유사합니다.
또한 async
함수 내에서이 작업을 수행해야 합니다.
내 B- 계획은 @slebetman 제안에 따라 생성자에서 데이터를로드하지 않고 초기화 함수를 사용하고 다음과 같이하는 것이 었습니다.
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());
});
}
피할 수 있다면 extend
클래스를 모두 피하고 함수 합성을 생성자 로 사용할 수 있습니다 . 클래스 멤버 대신 범위에서 변수를 사용할 수 있습니다.
async function buildA(...) {
const data = await fetch(...);
return {
getData: function() {
return data;
}
}
}
간단하게
const a = await buildA(...);
typescript 또는 흐름을 사용하는 경우 생성자 의 인터페이스를 적용 할 수도 있습니다.
Interface A {
getData: object;
}
async function buildA0(...): Promise<A> { ... }
async function buildA1(...): Promise<A> { ... }
...
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;
}
You may immediately invoke an anonymous async function that returns message and set it to the message variable. You might want to take a look at immediately invoked function expressions (IEFES), in case you are unfamiliar with this pattern. This will work like a charm.
var message = (async function() { return await grabUID(uid) })()
You can create an async init() {... return this;}
method, then instead do new MyClass().init()
whenever you'd normally just say new MyClass()
.
This is not clean because it relies on everyone who uses your code, and yourself, to always instantiate the object like so. However if you're only using this object in a particular place or two in your code, it should be fine.
@slebetmen's accepted answer explains well why this doesn't work. In addition to the two patterns presented in that answer, another option is to only access your async properties through a custom async getter. The constructor() can then trigger the async creation of the properties, but the getter then checks to see if the property is available before it uses or returns it.
This approach is particularly useful when you want to initialize a global object once on startup, and you want to do it inside a module. Instead of initializing in your index.js
and passing the instance in the places that need it, simply require
your module wherever the global object is needed.
Usage
const instance = new MyClass();
const prop = await instance.getMyProperty();
Implementation
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;
}
}
}
The other answers are missing the obvious. Simply call an async function from your constructor:
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>
`
}
You should add then
function to instance. Promise
will recognize it as a thenable object with Promise.resolve
automatically
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)
}
참고URL : https://stackoverflow.com/questions/43431550/async-await-class-constructor
'program story' 카테고리의 다른 글
Android에서 상태 표시 줄을 숨기는 방법 (0) | 2020.08.26 |
---|---|
iTunes Connect "계약, 세금 및 은행 업무"메시지가 사라지지 않음 (0) | 2020.08.26 |
식별자 Cell이있는 셀을 대기열에서 빼낼 수 없음-식별자에 대한 펜촉 또는 클래스를 등록하거나 스토리 보드의 프로토 타입 셀을 연결해야합니다. (0) | 2020.08.26 |
Javascript .filter () 메서드의 콜백 함수에 추가 매개 변수를 어떻게 전달합니까? (0) | 2020.08.26 |
Guid.IsNullOrEmpty () 메서드가없는 이유 (0) | 2020.08.26 |