getter & setter wrapper for settings

I'm trying to use a getter & setter pair as an interface for game.settings.get and game.settings.set. Is it possible to make the setter asynchronous so that I can use await when I made assignments to it. This is what my getter and setter currently look like:
class Ctg {
static get MODES() {
return game.settings.get(Ctg.ID, "modes");
}
static set MODES(value) {
return game.settings.set(Ctg.ID, "modes", value);
}
}
class Ctg {
static get MODES() {
return game.settings.get(Ctg.ID, "modes");
}
static set MODES(value) {
return game.settings.set(Ctg.ID, "modes", value);
}
}
I'd like to do something like this (pseudocode):
await ( Ctg.MODES = Ctg.MODES.push(["NPC", "isNPC"]) );
await ( Ctg.MODES = Ctg.MODES.push(["NPC", "isNPC"]) );
15 Replies
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
Daniel Thorp
Daniel Thorp•3y ago
game.settings.set returns a promise, but I think my code may be wrong because setters aren't supposed to return anything at all usually I just added the return because I was hoping to be able to await that promise later (as shows in my pseudo code). This works actually:
let obj = {
get value() {
return this._value;
},
set value(myPromise) {
return (async () => {
this._value = await myPromise;
})();
},
};
obj.value = new Promise(resolve => setTimeout(() => resolve("hi"), 1000));
let obj = {
get value() {
return this._value;
},
set value(myPromise) {
return (async () => {
this._value = await myPromise;
})();
},
};
obj.value = new Promise(resolve => setTimeout(() => resolve("hi"), 1000));
Not sure if awaiting the assignment is even possible, but that's what I want to do.
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
Mana
Mana•3y ago
I'd think setter's return value is ignored and any attempt get the value of it would invoke the getter. I'd honestly expect JS to runtime error from attempting to return in setter.
Daniel Thorp
Daniel Thorp•3y ago
No, it doesn't.
Mana
Mana•3y ago
That is bizarre.
Daniel Thorp
Daniel Thorp•3y ago
Is it impossible to do what I'm trying to do then? (await the assignment)
Daniel Thorp
Daniel Thorp•3y ago
There's an eslint rule that explains a bit: https://eslint.org/docs/rules/no-setter-return
ESLint - Pluggable JavaScript linter
no-setter-return - Rules
A pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript. Maintain your code quality with ease.
Mana
Mana•3y ago
Returning the promise seems the only possible solution. But promises are dumb when worked with directly. Even that says the returned value is ignored, or is an error (in the JS runtime). With setter/getter stuff your only option pretty much logically is to store the intermediate promise and return that in the getter. It's kinda like how constructors can't be async.
Daniel Thorp
Daniel Thorp•3y ago
Can you give an example of how I would do that?
Mana
Mana•3y ago
Something like
class Ctg {
static _t_modes;
static get MODES() {
return this._t_modes ?? game.settings.get(Ctg.ID, "modes");
}
static set MODES(value) {
this._t_modes = game.settings.set(Ctg.ID, "modes", value).then(_ => this._t_modes = null);
}
}
class Ctg {
static _t_modes;
static get MODES() {
return this._t_modes ?? game.settings.get(Ctg.ID, "modes");
}
static set MODES(value) {
this._t_modes = game.settings.set(Ctg.ID, "modes", value).then(_ => this._t_modes = null);
}
}
It makes the getter kinda meh Why do you even need the modes getter to delay for the arbitrary chance the setting is changed right then?
LukeAbby
LukeAbby•3y ago
https://stackoverflow.com/a/44578144/5461005 This is the closest you'll get.
Stack Overflow
How would one do async JavaScript getters and setters?
Think of how Rails, e.g. allows you to define a property as associated with another: class Customer < ActiveRecord::Base has_many :orders end This does not set up a database column for orders.
LukeAbby
LukeAbby•3y ago
I'd suggest against this If you need an asynchronous setter you'll probably just want to do:
class Ctg {
static get MODES() {
return game.settings.get(Ctg.ID, "modes");
}

static async setMODES(value) {
// Something using await here.
...
}
}

await Ctg.setModes(["NPC", "isNPC"]);
class Ctg {
static get MODES() {
return game.settings.get(Ctg.ID, "modes");
}

static async setMODES(value) {
// Something using await here.
...
}
}

await Ctg.setModes(["NPC", "isNPC"]);
In languages without getters/setters getX and setX is a convention for when you want something like that. It'll also crop up for when you want a more complicated getter/setter... like something asynchronous. The general use case of a getter/setter in my eyes is limited to very trivial operations because you don't want x.y taking 3 seconds mysteriously. x.getY() encourages people to cache the value.
TyphonJS (Michael)
TyphonJS (Michael)•3y ago
One point of distinction is that accessors IE getters / setters were introduced in ES5 in 2009. This is a long time before the async proposal dropped and hit stage 1 in the TC39 process. async / await semantics were introduced in the ES7 timeframe more officially. As far as JS goes it wasn't until ES7 / ~2016/17 that things have been accelerating in a more rapid yearly pace for introduction for more features and there was a 6 year gap between ES5 and ES6. Whereas ES6 formalized basic class representation of the existing accessor functionality of ES5. Outside of whether it would make sense in the first place for the ability to have an async setter if you were designing things from scratch right now any new feature like this has to be backward compatible to some extent with what existed before.
Daniel Thorp
Daniel Thorp•3y ago
Ok. I did what LukeAbby suggested and it seems to be working, so I'll archive this thread 🙂