Flex эквивалент ProcessMessages и невосприимчивый пользовательский интерфейс при длинных циклах
Я обнаружил, что мой интерфейс приложения Flex не реагирует на очень длинные циклы обработки (десятки секунд). Например, при обработке очень больших файлов XML и выполнении чего-то за элемент...
Есть ли эквивалент "ProcessMessages"? То есть, вызов, который подскажет, что Flex продолжит отвечать на события пользовательского интерфейса, даже в середине какого-то длинного цикла, чтобы пользовательский интерфейс не переставал отвечать на запросы?
Я знаю, что Flex однострочный по дизайну. Именно поэтому я ищу что-то вроде ProcessMessages() - функцию, которая позволяет однопоточным реентерабельным приложениям (например, в VB или однопоточных приложениях на основе C-конвейера) оставаться отзывчивыми в течение долгого времени операции.
Резюме ответов
- В Flex нет встроенной функции типа
HandleEvents()
или ProcessMessages()
.
- Использование своего рода механизма обратного вызова для итеративного обработки фрагментов длинного процесса вычисления, при одновременном обеспечении времени выполнения между кусками, что позволяет ему реагировать, является единственным способом поддерживать отзывчивый интерфейс во время длительных вычислений.
- Способы достижения вышеуказанного:
- Использование события
enterFrame
, которое вызывается всякий раз, когда слой Flash "movie" ниже приложения Flex обновляет его фрейм (что-то вроде 20fps).
- Использование таймера.
- Использование
UIComponent.callLater()
, которое планирует выполнение работы "позже". (как docs сказать: 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.
- Использование намеренно инициированных событий мыши/клавиатуры для создания псевдо-рабочих потоков, как в в этом примере.
Если есть дополнительные предложения или если я что-то упустил, отредактируйте эту (сейчас) часть вики, не стесняясь.
Ответы
Ответ 1
Проблема заключается в том, что Flash является однопоточным, то есть до тех пор, пока часть кода не будет запущена, никакая другая обработка не может быть выполнена. Вам нужно как-то разбить обработку на более мелкие куски и выполнить эти куски, скажем, в событии enterFrame
.
Изменить. Я боюсь, что опрокидывание этого (или Саймона) ответа не изменит того факта, что это невозможно в AS3. Прочтите эту статью, чтобы получить более подробную информацию о проблеме. Статья также включает в себя простую "библиотеку" под названием PseudoThread, которая помогает выполнять длинные фоновые вычисления. Однако вам все равно придется разбить проблему на более мелкие кусочки.
Ответ 2
Я могу сказать вам окончательно, что с Flex 3 нет встроенной конструкции, похожей на функциональность ProcessMessages
, которую вы описываете.
Самый распространенный способ обойти это - разделить всю работу, которую вы обрабатываете, в очередь "рабочих" задач и "рабочий менеджер" для управления очередью. По мере того, как каждый "рабочий" завершает свою обработку, диспетчер очереди рабочих выталкивает следующего рабочего из очереди и выполняет его при вызове callLater()
. Это будет иметь эффект, аналогичный "уступке основному потоку" и позволяющий вашему пользовательскому интерфейсу получать события и реагировать, сохраняя при этом обработку, когда управление возвращается работнику.
Как только вы это сделаете, вы можете провести некоторое тестирование и профилирование, чтобы выяснить, сможет ли ваше приложение выполнить запуск нескольких сотрудников перед вызовом callLater()
и инкапсулировать эту логику в диспетчере очереди рабочих. Например, в нашей реализации этого шаблона (с несколькими сотнями рабочих в очереди) мы смогли быстрее обработать обработку с сопоставимой воспринимаемой производительностью, выполнив 4 рабочих, прежде чем "перейти к основному потоку" с помощью callLater()
, но это полностью зависит от объема и характера работы, которую делают ваши работники.
Ответ 3
Модель процесса для ActionScript является однопоточной, поэтому ответ отрицательный. Лучше всего либо отложить до асинхронной задачи на сервере, если вы можете, либо вывести курсор ожидания во время выполнения вашего длинного цикла, либо разбить ваш процесс на несколько более мелких частей, которые не так навязчивы для пользовательского интерфейса, либо выполнить длительные задачи в тот момент, когда пользователь с меньшей вероятностью ощущает эффект (например, запуск приложения).
Ответ 4
ActionScript однопоточный по дизайну, никакое количество ответов downvoting не изменит это.
Для совместимости лучше всего попытаться разделить вашу обработку на более мелкие куски и сделать свою обработку итеративно.
Если вам абсолютно нужна потоковая передача, это может быть сделано в Flash Player 10, используя фильтры Pixel Bender. Они будут работать в отдельном потоке и могут дать вам обратный вызов после их завершения.
Я считаю, что они хорошо подходят для "хардкорных" задач обработки, поэтому они могут хорошо соответствовать вашей цели.
Тем не менее, они поставят целый ряд требований к вашему коду, так что вам может быть лучше делать небольшие "ведра" вычислений в любом случае.
Ответ 5
В Flash Player нет эквивалентных функций. По дизайну Flash Player чередуется между рендерингом на экран и затем выполняет весь код для каждого кадра. Использование событий Event.ENTER_FRAME
на экранных объектах или объектов Timer
в других местах - лучший выбор для распада очень длинных вычислений.
Стоит отметить, что некоторые события в ActionScript имеют функцию updateAfterEvent()
со следующим описанием:
Указывает Flash Player или среду выполнения AIR на визуализацию после завершения обработки этого события, если список отображения был изменен.
В частности, TimerEvent
, MouseEvent
и KeyboardEvent
поддержка updateAfterEvent()
. Могут быть и другие, но те, которые я нашел с быстрым поиском.