Rozwiązanie tymczasowe
Możesz utworzyć async init() {... return this;}
metodę, a potem robić new MyClass().init()
to, kiedy zwykle po prostu powiesz new MyClass()
.
Nie jest to czyste, ponieważ polega na tym, że każdy, kto używa Twojego kodu, i Ty zawsze tworzy instancję obiektu w ten sposób. Jeśli jednak używasz tego obiektu tylko w określonym miejscu lub dwóch w swoim kodzie, może to być w porządku.
Jednak pojawia się poważny problem, ponieważ ES nie ma systemu typów, więc jeśli zapomnisz go nazwać, po prostu wróciłeś, undefined
ponieważ konstruktor nic nie zwraca. Ups. Znacznie lepiej byłoby zrobić coś takiego:
Najlepszą rzeczą do zrobienia byłoby:
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}
Rozwiązanie fabryczne (nieco lepsze)
Jednak możesz przypadkowo zrobić nowy AsyncOnlyObject, prawdopodobnie powinieneś po prostu utworzyć funkcję fabryczną, która używa Object.create(AsyncOnlyObject.prototype)
bezpośrednio:
async function newAsync_AsyncOnlyObject() {
return await Object.create(AsyncOnlyObject.prototype).init();
}
newAsync_AsyncOnlyObject().then(console.log);
// output: AsyncOnlyObject {someField: 5}
Jednak powiedz, że chcesz użyć tego wzorca na wielu obiektach ... możesz to wyabstrahować jako dekorator lub coś, co (werbalnie, ugh) wywołasz po zdefiniowaniu postProcess_makeAsyncInit(AsyncOnlyObject)
, ale tutaj użyję, extends
ponieważ pasuje do semantyki podklas (podklasy to klasa nadrzędna + dodatkowa, w tym sensie, że powinny być zgodne z kontraktem projektowym klasy nadrzędnej i mogą robić dodatkowe rzeczy; podklasa asynchroniczna byłaby dziwna, gdyby rodzic nie był również asynchroniczny, ponieważ nie można go zainicjować tak samo sposób):
Rozwiązanie abstrakcyjne (wersja rozszerzeń / podklasy)
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}
(nie używaj w środowisku produkcyjnym: nie przemyślałem skomplikowanych scenariuszy, takich jak to, czy jest to właściwy sposób pisania opakowania dla argumentów słów kluczowych).