Interesting ChangeWatcher Behaviour

I've been working on a Flex app for visualizing market research data for the past week or two, and ran into an interesting behaviour of ChangeWatcher, quite at odds with how I'd have expected it to work.  As everyone knows, the Flash player is single threaded (and frame based), which makes for some interesting edge cases.  Part of that single threading is that each individual function invocation always happens within a single thread of execution, all within a single frame, and without any interleaving of other processing.  In simpler terms, a function always starts at the top and goes to the end, with sole control over the AVM. Events are handled, not as separate threads, but as a queue that gets processed at certain points (presumably the entry and/or exit of each frame).

With the ChangeWatcher, you can "watch" a given object property, and have a passed function (usually a closure) invoked when the property is reset.  Here's a simple example:

ChangeWatcher.watch(myObject, "nameArray", function(...args):void {
  Alert.show("myObject.nameArray was reset to " + myObject.nameArray);
});

That's all well and good, but what is interesting is that it happens as part of invoking the 'nameArray' setter on 'myObject'.  Note that the setter exists whether it's implicit or explicit.  I expected the callback to be event based, so the setter would execute and dispatch an event to be resolved at a later time.  It doesn't do that, the callback actually happens as part of the setter's execution.  This seems at odds with the event-driven nature of the Flash player.  Why does this matter, you ask?  Here's an example (assumed to be a method of the 'myObject' instance from above):

function doSomething():void  {
  nameArray = [];
  nameArray.push("barney");
  nameArray.push("heather");
}

I expected the behaviour of invoking 'doSomething' to go like this:

  1. set nameArray to a new Array instance
    1.  dispatch a change event to the change watcher
  2. add "barney"
  3. add "heather"
  4. return
  5. the event is received
  6. the ChangeWatcher callback is invoked (Alert "barney, heather")

However, because it's not event driven, that's not what happens.  Here's the actual behaviour:

  1. set nameArray to a new Array instance
  2. invoke the ChangeWatcher callback (Alert "")
  3. add "barney"
  4. add "heather"
  5. return

If you want the former behaviour, you have to reimplement the 'doSomething' method like this:

function doSomething():void  {
  var a:Array = [];
  a.push("barney");
  a.push("heather");
  nameArray = a;
}

That will give the desired behaviour, because when the watched setter is invoked, the array is already fully populated.  In this particular case (a contrived example), you could also do it this way:

function doSomethind():void {
  nameArray = [ "barney", "heather" ];
}

That's not going to be possible in a lot of cases, however.

2 responses to “Interesting ChangeWatcher Behaviour”

  1. manfred karrer

    i think that is because flash do not inspect the contetn inside the array. and the actual array object has not changed, it is treated as an reference. otherwise it would cost a lot of performance to inspect the whole object (it could be a complex graph)…