본문 바로가기

Blog/WYSIWYG Tool 개발

Queue에 의한 함수 실행 관리

이글은 hika님의 글 Message Queueing 서비스 를 읽다가 비슷한 작업을 하는 클래스가 있어 남겨봅니다. 기본적인 의도는 hika님이 설명하신바와 같습니다만 이 클래스의 사용처는 다음과 같습니다.

예를 들어 포토샾과 같은 프로그램들은 화면으로부터 끊임없이 사용자 입력(마우스 클릭과 같은) 이벤트를 받게 됩니다. 때문에 이런 이벤트들에 의한 코드 호출이 바로 hika님이 말한 "비순차적인 실행" 을 발생시킵니다. 포토샾에서 이미지에 어떤 효과를 적용하는 프로세스가 진행되는 도중에 사용자가 파일메뉴의 저장하기 명령을 다시 실행하게 된다면 저장되는 데이터는 예상과는 다른 데이터가 저장될 수도 있는 것이죠. 이런 문제들 때문에 저는 다음 두가지 완충장치를 두게 되었습니다.

  • 실행되는 Command를 일단 Queue에 저장한 뒤 실행을 관리한다.
  • Command가 실행되는 도중에는 화면에서 사용자 입력을 받지 못하도록 막는다.

위 두가지가 완전한 해결책이 될순 없지만 아직까지는 제 역할을 하고 있는 것 같습니다. 참고로 제가 pureMVC를 사용하고 있어서 Queue에 저장할 때 Command객체를 저장합니다.

코드

Command가 호출되었을 때 Command 객체 자체를 저장합니다.

 // ProcessManager Queue에 임시 저장
ProcessManager.caller(this); 
 

Command 실행이 끝나면 ProcessManager에게 종료를 알려 다음 process가 진행되도록 합니다.

 // command실행이 완료됬음을 알림
ProcessManager.end();

다음은 ProcessManager의 구현 코드입니다. 

        static private var _queueList:Array = new Array();

        static public function caller(command:IProcessCommand):void
        {
            
_queueList.push(command);
            
            
if (isProcess){
                
trace("다른 프로세스 진행중....wait : ", command);
                
return;
            }
            
            
try{
                
_nextProcess();
                
            }
catch(e:Error){
                
trace("*** Error : "+e.getStackTrace());
                
end();
                
            }
        }
        
        
static public function end():void
        {
            
_nextProcess();
        }
        
        
////////////////////////////////////
        // Private
        ////////////////////////////////////
        
        
//현재 실행중인 프로세스가 있는지 여부
        static private var _isProcess:Boolean = false;
        
protected static function get isProcess():Boolean
        {
            
return _isProcess;
        }
        
public static function set isProcess(value:Boolean):void
        {
            
if(_isProcess == value) return;
            
_isProcess = value;
            
            
if(_isProcess){
                
// 화면 입력 보호
                
protectedScreen();
            }
else{
                
// 화면 입력 보호 해지
                initScreen();
            }
        }

        
static private function _nextProcess():void
        {
            
// 더이상 실행할 프로세스가 없다.
            if (_queueList.length == 0){
                
_processEnd();
                
return;
            }
            
            
var command:IProcessCommand = _queueList.shift() as IProcessCommand;
            
_processStart(command);
        }
        
        
static private function _processStart(command:IProcessCommand):void
        {
            
trace("\n프로세스 실행=================", command, "\n");
            
isProcess = true;
            
command.processStart();
        }
        
        
static private function _processEnd ():void
        {
            
trace("\n프로세스 종료=================\n");
            
isProcess = false;
        }

아래 코드는 isProces에서 호출되는 화면에서 사용자 입력을 막아주는 부분입니다.

        ////////////////////////////////////
        // 화면 Protect 기능
        ////////////////////////////////////
        
        
static private var modalWindow:FlexSprite;
        
        
// 화면 활성화
        static private function initScreen():void
        {
            
if (modalWindow)
            {
                
var sm:ISystemManager = ISystemManager(FlexGlobals.topLevelApplication.systemManager);
                
var parent:Sprite = Sprite(sm.getSandboxRoot());
                
parent.removeChild(modalWindow);
                
                
sm.removeEventListener(Event.RESIZE, resizeHandler);
                
modalWindow = null;
            }
        }
        
        
// 화면 입력 막기
        static private function protectedScreen():void
        {
            
var sm:ISystemManager = ISystemManager(FlexGlobals.topLevelApplication.systemManager);
            
            
modalWindow = new FlexSprite();
            
modalWindow.name = "modalWindow";
            
modalWindow.alpha = 0.5;
            
modalWindow.tabEnabled = false;
            
            
const s:Rectangle = sm.screen;
            
const g:Graphics = modalWindow.graphics;
            
            
var c:Number = 0xFFFFFF;
            
g.clear();
            
g.beginFill(c, 100);
            
g.drawRect(s.x, s.y, s.width, s.height);
            
g.endFill();
            
            
sm.addEventListener(Event.RESIZE, resizeHandler);
            
            
var parent:Sprite = Sprite(sm.getSandboxRoot());
            
//var parent:Sprite = Sprite(FlexGlobals.topLevelApplication);
            parent.addChild(modalWindow);
        }
        
        
static private function resizeHandler(event:Event):void
        {
            
var s:Rectangle = ISystemManager(event.target).screen;
            
            
if (modalWindow)
            {
                
modalWindow.width = s.width;
                
modalWindow.height = s.height;
                
modalWindow.x = s.x;
                
modalWindow.y = s.y;
            }
        }

위에 구현된 코드는 오로지 호출된 순서에 의해서 실행순서를 강제하기 위한것입니다. 프로세스의 시작과 끝의 시점이 isProcess의 setter메서드에 집중된 만큼 화면 보호이외에 시간지연 아이콘을 표시하는 등에 활용도 생각해 볼 수 있겠네요. 기본 맥락은 hika님의 글을 참고하시고 유용하게 사용되었으면 좋겠네요.