Reputation: 23
tldr How can I set a class variable from within an Promise
I'm banging my head as I'm too stupid to use async/await
with javascript. There are of course plenty of examples and blog posts, but they are 'then'
inning the results only into console.log
, what I don't need.
My use case is very simple, I want to load translations from a json using fetch (if they haven't been loaded yet) and then return the translated value with a function translate
.
I thought if I use then
, then the execution is paused until the Promise
resolves or fails.
class Test {
constructor() {
}
/**
* Loads translations
*/
async _loadTranslations() {
console.log("Loading tanslations");
let response = await fetch('data.json');
let json = await response.json();
return json;
};
translate(key, language) {
if(!this.translation){
this._loadTranslations().then(data =>{
console.log("data is loaded!", data);
this.translation = data;});
}
return this.translations[language][key];
}
}
console.log("translation",new Test().translate("MEGA_MENU_CMD","de-DE"))
But it always logs translation undefined
.
I don't want something like
new Test()._loadTranslations().then(r => console.log("result",r))
as I want to use the translate function within templates and I don't want to use .then
in all of my templates.
Edit I don't see how I can use the fetch
to get data from an API and set it as the model of a class. The then
s and callback
s in my opinion do different things. Or how is the correct way to instantiate a class, load data from an API and then work with this data?
2nd edit:
In fact, I just want to create a TranslationMixin and load the translations once. Then in my page (I'm playing around with Polymer) I want to use return html '<div>${this.translate("de-De","propertyX"}<div>'
and that's why I don't want a Promise but just the plain string. In fact the translation should be loaded during construction and done. But as fetch
returns a Promise
I'm stuck with the Promise
and fail to get the value out (#sigh). So probably I stick just to regular XMLHttpRequest
...
Upvotes: 2
Views: 2109
Reputation: 664971
how is the correct way to instantiate a class, load data from an API and then work with this data?
Load data before instantiating the class, then the instance can use it synchronously.
You should load the translations in a static method before creating your instance, so that you won't have to wait for them in your translate
method:
class Test {
constructor(t) {
this.translations = t;
}
/**
* Loads translations
*/
static async fromLoadedTranslations() {
console.log("Loading tanslations");
let response = await fetch('data.json');
let json = await response.json();
console.log("data is loaded!", data);
return new this(json);
}
translate(key, language) {
return this.translations[language][key];
}
}
Test.fromLoadedTranslations().then(test => {
console.log("translation", test.translate("MEGA_MENU_CMD","de-DE"));
// or pass `test` to your polymer template
});
(async function() {
const test = await Test.fromLoadedTranslations();
console.log("translation", test.translate("MEGA_MENU_CMD","de-DE"));
// or pass `test` to your polymer template
}());
There's no way to load data asynchronously and not wait for it (using either then
or await
) somewhere.
Upvotes: 0
Reputation: 1169
This is the async/await solution you were looking for in the first place.
translate(key, language){
return this.translation
? this.translation[language][key]
: this._loadTranslations()
}
And since, you need to await the console.log too, and since await
can only be inside an async function, you can do the following ..
(async()=>{
console.log("translation", await new Test().translate("MEGA_MENU_CMD","de-DE"))
})()
Explanation
Function translate
returns the result of this._loadTranslations()
which is Promise
. Then, the console.log statement awaits that Promise and outputs once it is fulfilled, that is, once you have the JSON from data.json.
Upvotes: 0
Reputation: 337
If you want that you must put your translate Asynchronous aswell. So you need to use async and await on that function too.
Because he will actually do what your expect in the _loadTranslations() and he will await for a result, but since the function that is calling it is not async he will do everything till the end! I hope this helps.
Upvotes: 0
Reputation: 92450
You need to wait until the translation is finished. The easiest way to do that is to make translate
an async function as well. This will return a promise and you can get the translation in the then()
:
class Test {
constructor() {
}
/**
* Loads translations
*/
async _loadTranslations() {
console.log("Loading tanslations");
this.translation = "some translation"
return this.translation;
};
async translate(key, language) {
if(!this.translation){
return this._loadTranslations().then(data =>{
console.log("data is loaded!", data);
return data;});
}
return this.translation;
}
}
new Test().translate("MEGA_MENU_CMD","de-DE")
.then(translate => console.log(translate))
Upvotes: 2