One subject that comes up frequently from the proponents of either Silverlight (where C# and a variation of the .NET CLR would be in play) or JavaFX (which is layered on JavaSE capabilities) is that they will have the advantage of multi-threaded programming as a developer capability in the programming model.
Well, I dev manage and architect web RIA apps built using Adobe Flex. Naturally I have a different take on this subject:
Explicit Threading for Async Behaviors (Java/C#)
With Java and C# .NET developers, these folks are used to doing asynchronous operations on threads they explicitly create and manage, and then use the utility mechanisms that both Swing and C# .NET each provide to marshal any data to the main GUI thread.
Flex Async I/O
With ActionScript3/MXML based Flex apps, asynchronous operations revolve around doing I/O service calls or messaging interactions with the server-side.
So all the various I/O classes of the Flex SDK can operate in an asyncronous manner. For instance, the HttpService class has a send() method. Invoke send() and it will execute asychronously to the calling thread (which for Flex, will always be the main GUI thread of the app).
ActionScript3 Closures coupled with Flex Async I/O
To handle the result (or any error condition), you can use the closure feature of ActionScript3 language to supply a closure block that will process any result that is later asynchronously returned. The Flex architecture insures that your closure code that is invoked to process the result will actually execute on the context of the main GUI thread. Hence that code can safely interact with the state of Flex GUI objects.
A closure can be supplied to handle a successful business logic result, a business logic failure condition that is perhaps returned to the client - and a separate closure can be supplied to handle faults. Faults would occur because of, say, transport related failtures, etc. - such as not being able to connect to the destination server, I/O timeout, underlying socket errors, blah, blah, blah.
Closures are a very natural and intuitive means to program asynchronous class APIs.
Flex approach is a simpler programming model
The net result is that Flex architecture presents a simpler single-threaded programming model to the developer, while asyncrhonous I/O programming coupled with the language feature of closures, provides a means to create apps that are just as fluid and responsive to the user as are Java Swing or C# .NET WinForms apps. The user can continue to interact with the UI of the app, a progress indicator can be presented, and a cancel button that acutally works can be presented to the user.
So some of the same things can be accomplished in a Flex app but with a better and easier programming model than the explicit thread programming of Java or C# .NET.
Client-side MVC Pattern ala Flex
Just to put a fine point on this subject matter of Flex asynchronous style of programming, what a real Flex application will typically do is implement some variation of the MVC pattern (on the Flex client tier - not the server-side tier).
So the closure that processes the result of some asynchronous I/O service call operation (or a message pushed from the server-side via the Comet Pattern - ala BlazeDS), will typically use the result data to go and modify the state of an object that resides in the client. That object would be some aspect of the data state that constitutes the Model from the MVC pattern.
Properties, Events, Declarative Data Binding
Views onto the Model can utilize the AC3/MXML language features of properties, events, and declarative data-binding to couple interactions with the Model.
A Model object can expose its state as properties. When any I/O closure code modifies the state of Model objects, events on their properties will fire. Views can bind to these Model object properties such that Views will update themselves whenever the underlying Model objects change due to said asynchronous I/O activity.
Views can use a Controller to deal with feeding view changes (which are usually due to user interactions) into the underlying Model objects. Data validation will likely tend to reside in the View code. So View/Controller interaction will concern itself more with feeding user-driven changes in View state (that have been validated) back into underlying Model objects.
Multiple Views onto single Model
One upshot of the MVC pattern is that it now becomes straightforward to implement multiple Views onto the same underlying Model objects. Also, View code gets completely decoupled from I/O plumbing code, as that becomes partitioned into a separate aspect of the client app architecture.
Additional UI Considerations
Views may still need to be designed to say, disable a submit button after it's been pressed (so the user can't click it by accident or intent multiple times). Events could be used to signal to the View when the submit button can be safely enabled again. Likewise, there is the View programming involved for such things as showing progress indicators and/or cancel buttons when asynchronous I/O operations are actively being processed.