MaxArt2501 / object-observe

Object.observe polyfill
MIT License
351 stars 30 forks source link

You mentioned polling #8

Closed ORESoftware closed 9 years ago

ORESoftware commented 9 years ago

Hi - you mentioned object must be polled - can you explain if this implementing the same exact behavior as the future Object.observe functionality or are you just mimic-ing it? If you are polling objects then the latter must be true?

MaxArt2501 commented 9 years ago

You're correct, it's mimicking.

That's because we have no means to exactly replicate Object.observe since, it being a native function, it has access to internal low-level methods of data manipulation.

Any other O.o polyfill, and most of "data watcher" libraries, are based on dirty checking.

If you have any other question, ask away.

ORESoftware commented 9 years ago

Thanks. I do have another question :) I was under the impression that dirty checking was not polling but somehow hooked into objects the same way that O.o does, but non-natively. So I was surprised to see the mention of polling in this lib, because it seems unnecessary.

For example, Backbone uses getters and setters, and this puts in hooks in each of those functions so that events fire when attributes change.

But I guess if I set an attribute {}.x = 'fire' then there's no way to hook into that because it's the lowest level.

I am afraid to use anything with polling in it, and I wasn't under the impression that dirty checking was/uses polling?

ORESoftware commented 9 years ago

I went with the Polymer polyfill for O.o, but maybe you can tell me if it's polling or non-polling. I also just made the choice so I can easy change the choice.

ORESoftware commented 9 years ago

I am actually not very knowledgeable about computed properties and dirty checking. Accessors are just getters and setters TMK. But I don't know the difference between Ember's computed properties and Angular's dirty checking. And I don't know the difference between what those do and what this lib does.

ORESoftware commented 9 years ago

nevermind, I just learned this:

Angular defines a concept of a so called digest cycle. This cycle can be considered as a loop, during which Angular checks if there are any changes to all the variables watched by all the $scopes. So if you have $scope.myVar defined in your controller and this variable was marked for being watched, then you are explicitly telling Angular to monitor the changes on myVar in each iteration of the loop.

this makes me wonder, as you say, if the Polymer polyfill does just this? I am not sure...

ORESoftware commented 9 years ago

well, I can partly answer my question:

"(Polymer's) observe-js is a library for observing changes in JavaScript data. It exposes a high-level API and uses Object.observe if available, and otherwise performs dirty-checking. observe-js requires ECMAScript 5."

so I doubt I will use that either. I don't really want a separate loop running in program doing polling :(

MaxArt2501 commented 9 years ago

You're correct again. Both Angular's and Polymer's polyfills of Object.observe (which are not faithful polyfill like this one, but just routines shaped to fit the library's needs) are based on dirty checking/polling.

I don't know Ember enough, but computed properties are basically accessors - i.e. getters. If you have accessors for the properties you want to watch, you're basically golden. Accessors doesn't help catching deleted or added properties, though. Angular uses a different approach. Actually, Google pushed on Object.observe with the precise goal to improve Angular's performances.

For further informations, you can check the extended documentation.

For your project, if you're using either Polymer or Angular, forget about this polyfill and let the libraries take the burden of handle the variations in the data. If you're not, than the polyfill may be good for you indeed.

ORESoftware commented 9 years ago

Thanks for the info. Like I said, I am not super well versed in the front-end dev universe. However, one of the reasons (among many) that I chose Backbone was because it was more minimalist and didn't have a automatic two-way binding feature that seemingly degraded performance and did too much automagical stuff behind the scenes that I didn't understand. Now that I know that Angular requires a polling loop, I am happier about that decision.

I don't know much about Polymer except that it uses WebComponents - I had no idea that WC had anything to do with two-way binding or dirty checking.

I was going to use React with Backbone. I am 80% certain that React's design philosophy is to not using polling loops and two-way data binding. But I think it's pretty clear that Facebook did not have Object.observe in mind when creating React...whereas it seems that Angular 2.0 did have everything to do with Object.observe.

I will wait for Object.observe to fully arrive before messing with anything that uses automatic two-way binding using polling.

I guess my primary confusion arises from not understanding why polling is necessary for two way binding. From the model side, we can just uses accessors to fire events, which I view as just a higher level Object.observe. From the DOM side, we need to talk back to the models, and I guess maybe we have to poll those DOM elements for changes to talk back to the models? IDK

I guess that makes me wonder if there is not a native "Object.observe" for DOM elements

MaxArt2501 commented 9 years ago

There is... sort of. They're called DOM events, i.e. the usual click, keypress, input events and so on. Which means that if you're observing changes in the value property or similar, you can rely on them.

Don't use Object.observe on DOM elements. O.o is meant to observe... objects, i.e. something that does not have an easy way to broadcast changes in its properties (as opposed to DOM elements that have events). Before O.o the only way to do so was using accessors when possible, or dirty checking when it wasn't.

React's goal isn't to provide two-way data binding, but rather having a framework that eases the creation of resusable components. Backbone, on the other hand, provides two-way binding using models, that are predefined object which are heavily controlled by the framework. Angular, instead, uses simple objects as models, so it gives all the possible versatiliy that they give, but it also needs a way to bind data. That's why it needed Object.observe.

Polymer doesn't actually aim to provide two-way data binding: it provides a framework for web components. Granted, observe-js has been developed for Polymer, but when version 1.0 came out Polymer has been reduced to the bones and many features and functionalities have been put in separate modules, and everyone can decide whether using them or not.

The goal of this polyfill is to provide a bridge to developers until Object.observe will be supported by all the major browsers. This way, they can use a "standard" way to get hooks on data changes, that will flawlessly shift into the native version when it will be available.

ORESoftware commented 9 years ago

Thanks, that makes sense, it's very good info. Angular team is supposedly created a way to transition between 1.0 and 2.0 but I am not sure how that works.

ORESoftware commented 9 years ago

Hey Massimo - this conversation was very enlightening for me - so I thank you - I am using Backbone in the old school way but pairing it with React. Like you said, React doesn't go for two-way binding. In the case that two-way binding may be useful for the app I am working on, I started to incorporate Rivets. Rivets uses sightglass which is an object observer library https://github.com/mikeric/rivets. So there are a bunch of similar libs to yours, like Watch.js. Do you have any strong opinions on other libs like sightglass? Frankly I just wish Object.observe was already a thing, because I have trust issues with all the libs that are pre-empting it. I don't plan to use the Polymer O.o polyfill, because of this conversation, but since two-way data binding might prove to be very useful, I will probably try out Rivets.

Another thing that would be interesting to discuss, if you have a moment, is if there is any relationship to the "Object.observer pre-emptor libraries" and the Functional Reactive Programmings libs like Bacon.js and RxJS.

ORESoftware commented 9 years ago

By the way, to correct your previous statement, Backbone has one-way binding with models/collections. Your Backbone views can listen to model/collection events and then re-render. This is the most basic way to bind a view to a model. But out-of-the-box, Backbone does not have any prescription to do two-way binding, only one-way binding, binding the model to the view.

MaxArt2501 commented 9 years ago

Sightglass is a skeleton library that lets you implement your own "adapter" to observe the object properties you need to. Batteries are not included, which means that it comes with no adapters - but Rivets comes with one indeed (named .) that makes use of Object.defineProperty to catch changes in the property (i.e., defines a getter and a setter). It also wraps mutation methods like push and splice if the observed object is an array. In this it's surely faster and more efficient than dirty checking, but also less capable and more obtrusive than Object.observe.

In fact, Object.observe:

In the end, it all comes down to your needs. Object.observe is a very broad purpose tool. If you know exactly what properties may change, and don't care about property descriptors or prototype methods, then Rivets may be a good choice. But you may also consider defining the property descriptors on your own, for example:

function observeProperties(obj, props, callback) {
    props.forEach(function(prop) {
        var value = obj[prop];
        Object.defineProperty(obj, prop, {
            get: function() { return value; },
            set: function(newValue) {
                var oldValue = value;
                value = newValue;
                callback.call(obj, prop, newValue, oldValue);
            }
        });
    });
}

var test = {};
observeProperties(test, [ "foo", "bar" ], function(prop, value, old) {
    console.log(prop + " " + old + " -> " + value);
});

That's one simple property observer in just 13 lines of code.

Do you refer to something in particular with "Object.observer pre-emptor libraries"?

ORESoftware commented 9 years ago

by Object.observe pre-emptor libraries I just meant watch.js, this library, polymer (observe-js), sightglass, etc