tags:

views:

1525

answers:

5

I find that my Flex application's UI becomes unresponsive during very long processing loops (tens of seconds). For example, while processing very large XML files and doing something per-element...

Is there an equivalent of "ProcessMessages"? That is, a call that would tell Flex to continue responding to UI events, even in the middle of some long loop, so that the UI doesn't become unresponsive?

I'm aware Flex is single threaded by design. That's exactly why I'm looking for something like ProcessMessages() - a function that allows single-threaded reentrant applications (like in VB, or single-threaded message loop based C++ applications) to remain responsive during long operations.

Summary of Answers

  1. There's no built-in function like HandleEvents() or ProcessMessages() in Flex.
  2. Using some sort of callback mechanism to iteratively process chunks of a long computation process, while yielding to the runtime between chunks, thus enabling it to be responsive, is the only way to maintain a responsive UI during long computations.
  3. Ways of accomplishing the above are:
    1. Using the enterFrame event, which is called whenever the Flash "movie" layer below the Flex application refreshes its frame (which is something like 20fps).
    2. Using a timer.
    3. Using UIComponent.callLater() which schedules work to be done "later". (as the docs say: Queues a function to be called later. Before each update of the screen, Flash Player or AIR calls the set of functions that are scheduled for the update.
    4. Using intentionally triggered mouse/keyboard events to create pseudo "worker threads", as in this example.

If there are further suggestions, or if I left out anything, please feel free to edit this (now) wiki piece.

+2  A: 

The process model for ActionScript is single threaded, so the answer is no. Best bet is to either defer to an asynchronous task on the server if you can, or pop up a wait cursor while your long loop runs, or break your process into some smaller pieces which are not quite as intrusive to the UI, or perform the long running tasks at a moment when the user is less likely to feel the effect (app startup for instance).

Simon
The fact that it's single-threaded is no explanation for the lack of a ProcessMessages function. Such a function is suited _only_ for single threaded applications that have a message loop that processes user events. IOW, it works for single threaded VB applications so why not for Flex?
Assaf Lavie
+5  A: 

The problem is that Flash is single threaded, i.e. until a part of the code is running, no other processing can be made. You'll somehow need to break up the processing into smaller chunks and execute these chunks, say, on the enterFrame event.

Edit: I'm afraid that downvoting this (or Simon's) answer does not change the fact that this is not doable in AS3. Read this article for more insight on the problem. The article also includes a simple "library" called PseudoThread, which helps in executing long background computations. You still have to break up the problem into smaller pieces yourself, though.

David Hanak
+2  A: 

Actionscript is single threaded by design, no amount of downvoting answers will change that.

For compatibility your best bet is to try to split up your processing into smaller chunks, and do your processing iteratively.

If you absolutely need threading it can sort of be done in Flash Player 10 using Pixel Bender filters. These will run on a separate thread and can give you a callback once they are done.
I believe they are well suited for "hardcore" processing tasks, so they might fit your purpose nicely. However, they will put a whole other set of demands on your code, so you might be better of doing small "buckets" of computing anyways.

grapefrukt
Nobody argued about the fact that flex is single-threaded, jeez. Read the post again, it's _because_ it's single threaded that a message pump function is what's needed. The question is whether there is one. I don't need and don't want multiple threads in flex, just a way to make the ui responsive.
Assaf Lavie
+4  A: 

I can tell you definitively that as of Flex 3, there is no built-in construct similar to the ProcessMessages functionality you are describing.

The most common way to work around this is to split whatever work you are processing into a queue of "worker" tasks and a "worker manager" to control the queue. As each "worker" completes its processing, the worker queue manager pops the next worker off the queue and executes it in a callLater() invocation. This will have an effect that is similar to "yielding to the main thread" and allow your UI to receive events and be responsive, while still allowing the processing to continue when control is returned to the worker.

Once you've got this working, you can do some testing and profiling to figure out if your application can get away with executing a run of multiple workers before invoking callLater(), and encapsulate this logic within the worker queue manager. For example, in our implementation of this pattern (with a few hundred workers in the queue), we were able to get the processing done more quickly with a comparable perceived performance by executing 4 workers before "yielding to the main thread" with callLater(), but this is totally dependent on the scope and nature of the work that your workers are doing.

erikprice
And how do you implement callLater?
Assaf Lavie
It's a method on UIComponent. http://livedocs.adobe.com/flex/3/langref/mx/core/UIComponent.html#callLater()
erikprice
+1  A: 

There is no equivalent functionality in Flash Player. By design, Flash Player alternates between rendering to the screen and then running all of the code for each frame. Using Event.ENTER_FRAME events on display objects, or Timer objects elsewhere, are the best bet for breaking up very long calculations.

It's worth noting that some events in ActionScript have an updateAfterEvent() function with the following description:

Instructs Flash Player or the AIR runtime to render after processing of this event completes, if the display list has been modified.

In particular, TimerEvent, MouseEvent, and KeyboardEvent support updateAfterEvent(). There may be others, but those are the ones I found with a quick search.

joshtynjala