Comments on Object.observe strawman

# Kevin Reid (13 years ago)

MarkM asked for my perspective on the Object.observe strawman < strawman:observe>; here it is. These

remarks are written primarily for MarkM but I am CC'ing es-discuss as they may be of interest; please do not feel obligated to respond. I have not previously followed discussions of this proposed feature.

UI frameworks often want to provide an ability to databind objects in a

datamodel to UI elements. A key component of databinding is to track changes to the object being bound. Today, JavaScript framework which provide databinding typically create objects wrapping the real data, or require objects being databound to be modified to buy in to databinding. The first case leads to increased working set and more complex user model, and the second leads to siloing of databinding frameworks.

If the premise is that databinding should be possible without modifying the model objects, then accessor properties present a problem: at the object level (that is, not using Object.getPropertyDescriptor or such), accessor properties may be indistinguishable from data properties, but they cannot be observed generically. I see in "Goals" that accessor properties may be programmed to notify; but this gives additional complexity for property implementors (and, particularly, a way to work normally but fail in an "obscure" case). I do not see a realistic solution, but this is something to keep in mind. A similar issue is that proxies must implement notifications much more extensively, of course.

  1. Asynchronous notification of changes, but allow synchronous fetching of

changes pending delivery

This seems to me a very good goal. I'm currently working on an application which uses a custom notification framework extensively to keep derived values consistent with their source values. I have refrained from switching to asynchronous notifications, as I Should, because of concerns about fetching incorrect values during the window between the original field's notification and the derived field's update. Synchronous fetch allows for just-in-time updates, and also for laziness (with the caveat that the derived object cannot be GCed; I have designed a scheme for fixing this, but it requires the auditor facility).

However, provoked synchronous delivery is not actually sufficient for consistency, it seems to me. Suppose we have an original value cell (data property), a derived cell (accessor property) and a third cell derived from the second. Then if the first is assigned, the second is in a position to be consistent (because it may deliverChangeRecords in its getter — but it could just as well simply compare the current value to check for changes), but the third has no way to know that it should be updated, since the second does not signal its change. This could be fixed with an application-level synchronous notification protocol or transitive dependency calculator, but at that point I must ask the question:

What are the use cases for synchronous changes which are being considered?

Example

I note from the example that "updated" reports an oldValue but "reconfigured" does not report an oldPropertyDescriptor. This is not a lack of expressiveness but could be considered an inconsistency.

Object.unobserve

A new function Object.unobserve(O, callback) is added, which behaves as follows:

From a capability perspective, this has a certain weakness: it is not

possible to export a callback and allow multiple clients to make use of it (attach it to objects) independently, because having the callback also allows removal of it from an object. The alternative is to use a clearTimeout-style interface; the observe operation returns the option to cancel it. However, this can also be implemented by each client wrapping the callback with its own unique function, which moves the burden of additional objects to the less-common case, so the current definition is probably the right thing.

Object.deliverChangeRecords

A new function Object.deliverChangeRecords(callback) is added, which behaves as follows:

This means that a callback may be invoked without an empty stack, which is a hazard but seems acceptable as it is consistent with the above in suggesting that callbacks should be closely held.

[[ObserverCallbacks]]

There is now an ordered list, [[ObserverCallbacks]] which is shared per event queue. It is initially empty.

Should it be noted that the implementation is free to discard elements of [[ObserverCallbacks]] which are otherwise unreferenced (i.e. will never be invoked again)?

[[DeliverChangeRecords]]

[[DeliverAllChangeRecords]]

These algorithms return boolean results which do not seem to be used anywhere.

MarkM asked me to consider hazards arising from the interaction of this proposal and proxies. I have not thought of any such.