Nice, I made something really similar but found it had a bunch of issues. Some of which I don't see addressed here.
When setting an object or array, it will always trigger since the equal comparison will return false even if they have the same { key: value } pairs:
target[property] !== value
Speaking of which, sub-properties are not being listened to from the root (only if manually listened to on the sub-object):
obj.foo = { bar: 'baz' }; // triggers
obj.foo.bar = 'whatever'; // never triggers the base listener
The only realistic automatic way to listen to them is to add a trap for the get, which will proxify the return value if it's an object or array. This has it's own set of errors and turns into a nasty set of edge cases, which is the main reason I never got to release what I was doing. I see in Icaro you Proxify it manually and adding some props on the setter, which leads to this not working as expected:
Isn't this just queuing up the actual, real work on the event loop?
Of course its faster to call setImmediate a bunch of times to defer work, than it is to actually DO the work. It is useless to compare a synthetic benchmark for what is fundamentally a task that involves drawing something to the screen, without measuring the time it takes to finish all those tasks. The actual performance will, of course, be determined by how many DOM mutations are occurring, and the relative layout complexity of each. There will be a benefit for lots of really small, expensive mutations that could be batched together, but who designs their applications like that? Oh, wait...
At least, that's my understanding of looking at the source for a few minutes. Maybe I'm missing something, but I don't see any real algorithm at work here, just work that is shifted around to make a benchmark look good.
A better benchmark would be to use this in a real world, non-trivial use-case somehow and measure the framerate. But I can save you the effort: It's not going to win out over a well designed application that does direct DOM manipulation. Period.
But is Icaro more on listening changes on javascript objects, in a general manner, better than just the DOM ones?
I think that this light lib could be actually a cool toy to use if you need for example to do some simple app state listened manipulations on the node backend side.
Otherwise, that is true, I would maybe still prefer to use frontend side some well-designed frameworks like redux-Rx or redux-react
Last time I tried Proxies, their performance wasn't very good. It would be great to see a few benchmarks.
What advantages would this give you over using persistent data structures [0] with memoized selectors? When using immutable data structures, you can shift the responsibility of providing updates up to the caller. In my experience, that usually means code that's easier to debug and test.
I sort of understand why you want to avoid GitHub, thank you for sharing those links. All in all though to make a small pull request like the one in question, it takes about 2 minutes and three clicks to do so.
Not that you should do so if you don't want to, but this is a good thing for OS projects. The barrier to entry and contribute is non-existent - if you want to fix something small just press 'edit', make your changes in the browser and press 'submit merge request'.
No cloning, no local installs, no fuss. You can do it from your phone, on a bus.
ehy thanks for linking to your project it looks great! Regarding the batching strategy icaro smartly groups the object changes via setImmediate helping in case you need to change many properties on the same object for example:
obj.name = 'foo'
obj.surname = 'bar'
in this case icaro will dispatch only one event optimizing the listeners to avoid unnecessary redundant events
When setting an object or array, it will always trigger since the equal comparison will return false even if they have the same { key: value } pairs:
Speaking of which, sub-properties are not being listened to from the root (only if manually listened to on the sub-object): The only realistic automatic way to listen to them is to add a trap for the get, which will proxify the return value if it's an object or array. This has it's own set of errors and turns into a nasty set of edge cases, which is the main reason I never got to release what I was doing. I see in Icaro you Proxify it manually and adding some props on the setter, which leads to this not working as expected: