Flex中的ProcessMessages等效方法和长循环期间无响应的用户界面

11
我发现我的Flex应用程序在非常长的处理循环(数十秒)期间会变得不响应。例如,在处理非常大的XML文件并对每个元素执行某些操作时...
是否有类似于“ProcessMessages”的东西?也就是说,是否有一种调用方式可以告诉Flex在某个长循环中继续响应UI事件,以使UI不会变得不响应?
我知道Flex是按设计单线程运行的。这正是为什么我正在寻找像ProcessMessages()这样的东西 - 一种允许单线程可重入应用程序(如VB或基于单线程消息循环的C++应用程序)在长时间操作期间保持响应的函数。
答案摘要
  1. Flex中没有像HandleEvents()ProcessMessages()这样的内置函数。
  2. 使用某种回调机制来迭代处理长时间计算过程的块,同时在块之间让出运行时,从而使其响应,是保持响应式UI在长时间计算期间的唯一方法。
  3. 完成上述任务的方法有:
    1. 使用enterFrame事件,在Flex应用程序下面的Flash“movie”层刷新其帧时调用(类似于20fps)。
    2. 使用计时器。
    3. 使用UIComponent.callLater(),它会安排稍后执行的工作。 (文档: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. 使用有意触发的鼠标/键盘事件创建伪“工作线程”,就像this example(此示例)中所示。

如果有进一步建议或我遗漏了什么,请随时编辑这个(现在的)Wiki部分。

5个回答

6
问题在于Flash是单线程的,也就是说,在代码的一部分运行之前,不能进行任何其他处理。你需要将处理过程分解为更小的块,并在enterFrame事件上执行这些块。 编辑:恐怕对此进行投票(或Simon的回答)并不能改变这个事实,即AS3中无法做到这一点。阅读this article以获取有关该问题的更多见解。该文章还包括一个简单的“库”称为PseudoThread,可以帮助执行长时间的后台计算。但是,您仍然需要自己将问题分解为较小的部分。

是的,但同时,我能否在Flex中创建一个自定义事件,并将所需的代码附加到此自定义事件处理程序?并在每个“n”秒后触发该事件。这将确保它是异步调用,不必真正关心调用哪个函数,因为事件处理程序本质上是异步的。 - Neeraj
不,AS3中的事件处理程序并不是异步的,因为它们不会中断任何正在运行的AS3代码。事件被收集在事件队列中,第一个事件被取出,其处理程序被完全执行,然后引擎继续处理下一个事件。 - David Hanak

4
我可以明确告诉你,从Flex 3开始,没有类似于您所描述的ProcessMessages功能的内置构造。

最常用的解决方法是将您正在处理的任何工作拆分为“工人”任务队列和“工人管理器”,以控制队列。当每个“工人”完成处理时,工人队列管理器会弹出下一个工人队列,并在callLater()调用中执行它。这将产生类似于“让主线程让步”的效果,使您的UI能够接收事件并响应,同时在返回控制权给工作线程时仍允许处理。

一旦您实现了这个,您可以进行一些测试和分析,以确定您的应用程序是否可以在调用callLater()之前执行多个工作线程的运行,并将此逻辑封装在工人队列管理器中。例如,在我们实现这个模式时(有几百个工人在队列中),我们能够通过在“让主线程让步”之前执行4个工人来更快地完成处理,并具有相当的感知性能,但这完全取决于您的工人所做工作的范围和性质。


你如何实现callLater? - Assaf Lavie
这是UIComponent上的一个方法。http://livedocs.adobe.com/flex/3/langref/mx/core/UIComponent.html#callLater() - erikprice

2

对于ActionScript的进程模型是单线程的,所以答案是否定的。最好的方法是,如果可以的话,延迟到服务器上的异步任务,或者在长循环运行时弹出等待光标,或者将您的过程分解成一些对UI不那么侵入性的较小部分,或者在用户不太可能感受到影响的时刻执行长时间运行的任务(例如应用程序启动)。


它是单线程的事实并不能解释为什么缺少ProcessMessages函数。这样的函数仅适用于具有处理用户事件的消息循环的单线程应用程序。也就是说,它适用于单线程的VB应用程序,那么为什么不适用于Flex呢? - Assaf Lavie

2
ActionScript 是单线程设计的,无论如何,否定答案都不会改变这一点。
为了兼容性,最好的方法是将处理过程分成较小的块,并进行迭代处理。
如果您绝对需要线程,则可以在 Flash Player 10 中使用 Pixel Bender 滤镜实现。这些将在单独的线程上运行,并且可以在完成后给您回调。 我认为它们非常适合“重度”处理任务,所以它们可能非常适合您的目的。 但是,它们将对您的代码提出另一组要求,因此您最好仍然进行小的“计算桶”。

没有人会争辩Flex是单线程的事实,天哪。再读一遍帖子,正因为它是单线程,所以需要一个消息泵函数。问题是是否有这样一个函数。我不需要也不想在flex中使用多个线程,只需要一种使UI响应的方式。 - Assaf Lavie

1

Flash Player中没有等效的功能。按设计,Flash Player在渲染到屏幕后会运行每个帧的所有代码。在显示对象上使用Event.ENTER_FRAME事件或其他地方使用Timer对象是分解非常长计算的最佳选择。

值得注意的是,ActionScript中的一些事件具有updateAfterEvent()函数,其描述如下:

如果显示列表已被修改,则指示Flash Player或AIR运行时在处理此事件完成后进行渲染。

特别是TimerEvent, MouseEvent, 和 KeyboardEvent支持updateAfterEvent()。可能还有其他事件,但这些是我快速搜索找到的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接