I an using ES6 classes plus Object.observer, using the MaxArt2501 implementation. I have this code below:
const READY = Symbol("Ready");
const RUNNING = Symbol("Running");
class Foo {
constructor() {
this.value = 1;
this.status = READY;
}
bar() {
this.status = RUNNING;
console.log("bar", this.status );
this.value = this.value + 10;
this.status = READY;
console.log("bar", this.status );
}
}
let foo = new Foo();
Object.observe(foo, function(changes) {
changes.forEach(function(change) {
console.log("foo: ", change);
});
});
My expectation was that when I execute the bar()
method, the observer function would be called 3 times: When changing foo.status
from READY
to RUNNING
, when changing foo.value
and when changing foo.status
from RUNNING
to READY
.
However, this is what I got when running bar()
(In Chrome console):
> foo
Foo {value: 1, status: Symbol(Ready)}
> foo.bar()
bar Symbol(Running)
bar Symbol(Ready)
undefined
foo: Object {name: "value", type: "update", object: Foo, oldValue: 1}
> foo
Foo {value: 11, status: Symbol(Ready)}
The undefined is the return from bar()
method. But any way, it looks like just the changing in foo.value
is really observed, but not foo.status
.
If I change foo.status
value "manually" it is observed:
> foo.status = RUNNING
Symbol(Running)
foo: Object {name: "status", type: "update", object: Foo, oldValue: Symbol(Ready)}
> foo
Foo {value: 11, status: Symbol(Running)}
> foo.status = READY
Symbol(Ready)
foo: Object {name: "status", type: "update", object: Foo, oldValue: Symbol(Running)}
> foo
Foo {value: 11, status: Symbol(Ready)}
I could say that when a property class is changed inside the class it does not notify its observer. However, the foo.value
change in bar()
method was observed.
Does someone have any idea about what is happening here?
Thanks,
Rafael Afonso
UPDATE - 06/06
I did a workaround which, although not exactly very elegant, I could use as base in my real implementation:
const READY = Symbol("Ready");
const RUNNING = Symbol("Running");
class Foo {
constructor() {
this.value = 1;
this.status = READY;
}
bar() {
// this.status = RUNNING;
console.log("bar", this.status );
this.value = this.value + 10;
// this.status = READY;
console.log("bar", this.status );
}
}
let foo = new Foo();
Object.observe(foo, function(changes) {
changes.forEach(function(change) {
if(change.name === 'value') {
change.object.status = RUNNING;
}
console.log("foo: ", change);
});
});
When I run, I got this:
> foo.bar()
bar Symbol(Ready)
bar Symbol(Ready)
undefined
foo: Object {name: "value", type: "update", object: Foo, oldValue: 1}
foo: Object {name: "status", type: "update", object: Foo, oldValue: Symbol(Ready)}
As you see, I change the foo.status
in observable method. However, apparently just after the foo execution, both properties changes are notified.
I was thinking if would not be better use a approach such as Observable and Observer in Java. Does somebody know some JS library which implements in this way?
UPDATE - 06/06 (2)
As suggested, I tried use proxies:
const READY = Symbol("Ready");
const RUNNING = Symbol("Running");
class Foo {
constructor() {
this.value = 1;
this.status = READY;
}
bar() {
this.status = RUNNING;
// console.log("bar", this.status );
this.value = this.value + 10;
this.status = READY;
// console.log("bar", this.status );
}
}
let observer = {
set: function(obj, prop, value) {
let oldValue = obj[prop];
let result = Reflect.set(obj, prop, value);
console.log(prop + ": " + oldValue.toString() + " -> " + value.toString());
return result;
}
}
let foo = new Proxy(new Foo(), observer);
When I runned, I got this:
> foo.bar()
status: Symbol(Ready) -> Symbol(Running)
value: 1 -> 11
status: Symbol(Running) -> Symbol(Ready)
undefined
> foo.bar()
status: Symbol(Ready) -> Symbol(Running)
value: 11 -> 21
status: Symbol(Running) -> Symbol(Ready)
undefined
Indeed, this is what I was looking for. Although the Object.observer goals to intercept all kind of changes in the target, I am interested just in the properties setting.
A quick skim of that repo suggests that updates are only performed once per frame (in environments with such a notion) or once every 17ms.
Because JavaScript is single-threaded, if you change a property and and then change it back within a single uninterrupted function (i.e. not a generator etc), it will not be observable by this package.
Keep in mind that Object.observe is no longer on track to become part of the language. An actual implementation of the proposed spec could have given you notifications for each event, but it's not possible to fake for this specific situation (at least without wrapping every object in a proxy).