Dealing with asynchronous events, part 3
I’d like to talk about some improvements to the RPC call mechanisms in Flex 2. Matt Chotin once again proved to me that he knows more about Flex than I will ever know by kindly pointing out that the signature of the RPC calls in Flex 2 have changed to formally return objects of type AsyncToken, which has a built-in responder mechanism.
~
Using AsyncToken and Responders
To use the mechanism as-is, you do something like this:
public function getAlbumInfo(albumId: int) : void { // Initiate the call. myService.request = { type: "album", albumId: albumId }; var call : AsyncToken = myService.send(); // Save the albumId for use later. call.albumId = albumId; // Wire up the handler. call.responder = new Responder(getAlbumInfoResult, null); } public function getAlbumInfoResult(event: Event) : void { // Deal with the results of the previous call. var artistId: int = event.result.album.artistId; myAlbumList.addAlbum(event.call.albumId, event.result.album); // Initiate the next call. myService.request = { type: "artist", artistId : artistId }; var call : AsyncToken = myService.send(); // Save the artistId for use later. call.artistId = artistId; // Wire up the handler. call.responder = new Responder(getArtistInfoResult, null); } public function getArtistInfoResult(event: Event) : void { // Handle the results of the second call. myArtistList.addArtist(event.call.artistId, event.result.artist); }
You’ll notice that it looks a lot like the first example in the previous post. The main difference is that the plumbing to make the handlers go is done in the framework, not in your code. (in the earlier example, I’d omitted the plumbing).
If we had anonymous inner classes…
If ActionScript had anonymous inner classes, you could collapse this into something like this, which is similar to the closure method I’d mentioned earlier:
public function getAlbumInfo(albumId: int) : void { var call: AsyncToken; // Initiate the first call. myService.request = { type: "album", albumId: albumId }; call = myService.send(); call.responder = new IResponder() { public function result(data:Object):void { // Handle the results of the first call. var artistId: int = event.result.album.artistId; // Notice we can use albumId here directly. myAlbumList.addAlbum(albumId, event.result.album); // Initiate the second call. myService.request = { type: "artist", artistId: artistId }; call = myService.send(); call.responder = new IResponder() { public function result(data:Object):void { // Handle the results of the second call. myArtistList.addArtist(artistId, event.result.artist); } } } } }
Even with anonymous inner classes (which is not something that ActionScript 3 has), I find this harder to read than the closure approach, so I think I’m going to stick with that.
Sho,
Hi. I’m just going to be a pain here and point out one of my concerns with AS3.0: those wacky dynamic classes!
Specifically, in this instance it seems that the API is not very future proof. AsyncToken is dynamic and coders are encouraged to shove their own properties in it if they want access to them on the other side of the the asynchronity. It seems to me that it would have been much safer to make AsyncToken a sealed class and provide another Object property in it called ‘userData’ for storage. Your code would change in the following way: “call.userData.artistId = artistId”.
My reasoning is that a coder using AsyncToken has to be careful not to set any property called “message”, “responder”, or “result”. If they’re doing something clever and adding properties that are determined at runtime, they’d have to specifically add code to translate any of those names to some safe string.
Similarly, if the API changes in the future and someone over at Adobe adds a new property to AsyncToken called “artistId” then suddenly your code breaks. This sucks.
Thoughts?
Makes sense. “artistId” is not something that’s likely to get clobbered, but what if you add a “name” field to the AsyncToken object?
I think this is a valid point for AsyncToken and I’ll bring it up with team. Just to set expectations, it may be too late in the cycle to address, or there may be good reasons for doing things in the way that we have (e.g., backwards compatibility with the old way we did things, in which the call object was an untyped blob).
Sho,
Respectfully (because I know you guys are doing a great job over there), I don’t buy the “backwards compatibility” argument because already a lot of major things have changed since AS2.0. Classes are now sealed by default, __resolve and __proto__ were removed from Object, the “for in” loop behaves differently, caller was removed from the arguments class, and equality operators behave slightly differently in some cases. It’s a whole new language.
I do understand that it might be too late to make any changes, big or otherwise.
But if I had any say, I’d be arguing for a massive change: make Object sealed and make a new dynamic subclass called AssocHash or ObjHash or something nicer. The fact that currently the base class is dynamic utterly screws the compiler from finding many errors. It’s all too easy to pull an element out of an array and forget to cast it before calling some method on it (array[i].getArtistInfo(), say). Since the reference is to a dynamic class (Object) the compiler has no choice but to just let it pass. At runtime it may work, or it may not if the method name was mistyped.
I’m working on large project in ActionScript 3, and I’ll take any help the compiler can give me. Requiring the programmer to identify references that should be given a “free pass” (by casting them to a dynamic class, up from the theoretically sealed Object) would make life easier for any large project. I guess the downside is that it gets a little bit more complicated for artist types that just want to write a few simple lines of code for their movie.
Another strange bit is that a sealed class may be a subclass of a dynamic class. If I have a reference to an object that is sealed but it is typed as its superclass, then the compiler will think that I can assign new properties and will happily let me do so. Only at runtime will I discover that the instance is not dynamic when the VM throws an error. Of course, it will work sometimes (if the caller passes the base class or a subclass that is correctly dynamic) and not others (if the caller passes a subclass that is sealed). Obviously this is easily fixed (assuming the caller correctly deciphers the runtime error message) by making the subclass dynamic, but it’s just one more thing standing in the way of easy development.
In magical RayLand, where Object is sealed, the language spec could also be changed so that dynamic classes may not have sealed subclasses, and this particular problem goes away.
Anyway, that’s just my 2 cents: I think ECMA compliance is a bullet-point feature that could easily be binned. People aren’t choosing to develop in flash because the language is standards compliant, they’re choosing to develop in flash because of what it allows them to do on the web. I’d personally rather have a language that helped me find bugs before runtime than to have one that happens to be somewhat similar to javascript.
Peace,
Ray
Hi Ray. Great comments.
First, the issues around AsyncToken. I checked with the folks responsible for AsyncToken. While there is a small chance that future changes might lead to name conflicts, (a) they are not planning on any changes in AsyncToken from here on out, and (b) it is too late in the cycle to change it for this release, regardless.
About dynamic objects in general and ECMA compatibility: I think ECMAScript compatibility is very important, and will continue to grow more important. The Mozilla guys are moving forward with changes to the JavaScript language, and we are working with them to craft and define this new language. Because of the history of JavaScript/ECMAScript, Object needs to be a dynamic class. However, there are language changes on the way (such as typed arrays) that will lead to fewer and fewer uses of the Object type in the future, if that’s what you want.
You’re right to point out the strangenesses that can occur because dynamic is not necessarily inherited. If you start with the notion that the root base class (Object) is a dynamic class, you have to do something to disallow inheriting of dynamic at some point, or else all classes end up being dynamic. Internally at Adobe (which was Macromedia at the time), I had pushed for making all subclasses of dynamic objects dynamic, except in the case of the Object class. Others thought that making an exception for Object was weird. I can see both sides, but there you have it.
All these issues come into being when you try to combine the concepts of strong typing with a dynamic language. There are benefits to using a dynamic language (witness the cool tricks you can do with Python or Ruby), and I’d hate to lose them all in the queest for strong typing.
I don’t understand why the code always contains closures instead of prototypes. See “prototypes instead of closures” section here:
http://www.softwaresecretweapons.com/jspwiki/Wiki.jsp?page=JavascriptRefactoringForSaferFasterBetterAJAX
Hi Mike. Thanks for the great question.
The article you cite is really not related to the use of closures above.
In traditional JavaScript, there are two ways to add methods to objects: closures and prototypes. Adding methods to objects by using prototypes is more efficient than using closures to do this.
In ActionScript (and the upcoming JavaScript 2), there are actually three ways to add methods to objects: class declarations, prototypes, and closures. Adding methods directly to the class declaration is by far the most efficient way to do this, especially when type decorations are used.
The use of closures cited above is about controlling program flow for asynchronous callbacks. This is not an area where prototype (or methods defined through class declarations) can really help.
Furthermore, the issue that is raised in the article about runtime cost is not really valid here. The issue cited in the article is the instances of closures are kept alive for each instance of the object. If you multiply the number of objects that are alive at any given moment by the number of methods on each object, you will see that a lot of closures will have to be kept alive if you aren’t careful.
For what I was talking about (which, again, is not the same thing that the article was talking about), closures only remain for during the time between when an asychronous call is made to when it is handled.
Does this answer your question?
Sho,
Thanks for taking the time to respond to me. I can respect the desire to keep the language a dynamic language at its core.
I wonder if it would be possible to add a new compiler directive called -warndynamic, or -superstrict or something, that warned of any accesses to properties that are not defined at compile time. Just a thought.
thanks sho, for taking the time to explain that to me. I understand it better now. -mike.
Anyway, that’s just my 2 cents: I think ECMA compliance is a bullet-point feature that could easily be binned. People aren’t choosing to develop in flash because the language is standards compliant, they’re choosing to develop in flash because of what it allows them to do on the web. I’d personally rather have a language that helped me find bugs before runtime than to have one that happens to be somewhat similar to javascript.
Hi Sho,
I come back and want to translate this series of posts into Chinese.Your posts are so great.
Thanks
[…] Note that this is a more basic (and common?) case than the stuff I was talking about earlier. For more advanced solutions, see here, here and here. From: XXXX@XXXXXXXXX […]
OK, call me stupid. Just starting with FLEX…
According to the LiveDocs, AsyncToken does NOT have a responder property. It does have a responders property but it is a read-only array. It also has an addResponder() method which SHOULD allow the code in your example to work something like this:
call.addResponder(new Responder(getAlbumInfoResult, null));
Unfortunately, this doesn’t work either. FlexBuilder complains with “1067: Implicit coercion of a value of type flash.net:Responder to an unrelated type mx.rpc:IResponder.”. I don’t have enough understanding of the language yet to resolve this issue so will have to try something else.
Could you please indicate how this is supposed to work. Thanks.
Through blind luck I managed to get it working:
call.addResponder(new mx.rpc.Responder(getAlbumInfoResult, null));
Still trying to work out why you have to add a reference to the package. I tried using an “import mx.rpc.Responder;” statement but got a different error which seemed to indicate there was more than one Responder class and that the compiler wasn’t sure which one I meant. Thanks again for the great article.
[…] Dealing with asynchronous events, part 3 […]
[…] 原文地å€ï¼šhttp://kuwamoto.org/2006/05/19/dealing-with-asynchronous-events-part-3/ […]
[…] the issues of quot;Dealing with asynchronous eventsquot;. So far, he has Part 1, Part 2, and Part 3 available. I strongly recommend reading these articles. They provide a lot of good information for […]
Dealing with asynchronous events,