Flex timer is inaccurate – sample solution


flash_flex_air

Timer is, without a doubt, one of the most useful classes in Action Script 3 (Flex and AIR frameworks). It allows you to run various tasks at specific time or repeat them in a loop for a predefined intervals. I know, it’s absolutely nothing special these days, cause it’s available in most modern programming languages, like for example in Java. And that’s why it came with a bit of a shock to me, that apparently AS3′s Timer class seems to be corrupted! I mean, it definitely is working very stable and it has all the methods you need to control it’s flow. But in some cases it’s very difficult to make it work as precisely as you would want it to.

As a proof, I’m going to describe how I’ve been trying to create a very simple countdown clock in Flex. It’s main task was to countdown from the specified start up time, until it went down to zero. And everything seemed to be working great until the day when I’ve decided to measure the precision of my “timer based” countdown clock. After few tests I was shocked, cause my clocked seemed to be working very inaccurate. I’ve published a sample Flex app below, so you can try it yourself. Just type in any integer number, which will represent the starting point the timer will be counting down from. Check out the start and finish time values, which will be printed once you press the “Go” button. Try testing this application with 10 and 30 seconds values for example, and you’ll see how this delay is getting bigger, and bigger.

Here’s the source code of this very simple (one might say) application. As you can see, there’s nothing special in it that could be spoiling my clock.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="358" height="202"
	 creationComplete="initApp()" viewSourceURL="srcview/index.html">

	<mx:Label x="10" y="38" fontSize="22" fontWeight="bold" width="300" height="43" id="timerLabel"/>
	<mx:Label x="10" y="101" text="Seconds" id="secondsLabel"/>
	<mx:TextInput text="10" x="69" y="99" id="timerDuration"/>
	<mx:Button x="237" y="99" label="Go" id="goButton" click="this.restartTimer(int(timerDuration.text))"/>
	<mx:Label x="10" y="10" text="Inaccurate countdown clock" fontWeight="bold" fontStyle="italic"/>
	<mx:Label x="10" y="141" id="startTime" width="338"/>
	<mx:Label x="10" y="167" width="338" id="finishTime"/>

	<mx:Script >
		<![CDATA[
		 private var myTimer:Timer = new Timer(100);
		 private var mySeconds:int;
		 private var myMiliseconds:int;

		 private function initApp():void
		 {
		  myTimer.addEventListener(TimerEvent.TIMER,timerHandler);
		  myTimer.addEventListener(TimerEvent.TIMER_COMPLETE,timerCompleteHandler);
		 }

		 private function restartTimer(duration:int):void
		 {
		  this.myTimer.reset();
		  this.mySeconds = duration;
		  this.myMiliseconds = 100;
		  this.myTimer.repeatCount = duration*10;

		  if ( this.mySeconds < 10 ) this.timerLabel.text = "0"+this.mySeconds+":00";
		  else this.timerLabel.text = ""+this.mySeconds+":00";	

		  var myDate:Date = new Date();
		  this.startTime.text = "Start time (hour/min/sec/mili) - "+(myDate.hours+":"+myDate.minutes+":"+myDate.seconds+":"+myDate.milliseconds);
		  this.finishTime.text = "";

		  this.myTimer.start();
		 }

		 private function timerHandler(event:TimerEvent):void
		 {
		  if ( this.myMiliseconds == 100 )
		   {
		    this.mySeconds--;
		   }

		  this.myMiliseconds -= 10;

		  if ( this.myMiliseconds != 0 )
		  {
		   if ( this.mySeconds < 10 ) this.timerLabel.text = "0"+this.mySeconds+":"+this.myMiliseconds;
		   else this.timerLabel.text = ""+this.mySeconds+":"+this.myMiliseconds;
		  }
		  else
		  {
		   if ( this.mySeconds < 10 ) this.timerLabel.text = "0"+this.mySeconds+":00";
		   else this.timerLabel.text = ""+this.mySeconds+":00";
		  }

		  if ( this.myMiliseconds < 10 ) this.myMiliseconds = 100;
         }		 

         private function timerCompleteHandler(event:TimerEvent):void
         {
		  var myDate:Date = new Date();
		  this.finishTime.text = "Finish time (hour/min/sec/mili)- "+(myDate.hours+":"+myDate.minutes+":"+myDate.seconds+":"+myDate.milliseconds);
         }
		]]>
	</mx:Script>
</mx:Application>

As you could see, with 10 seconds countdown, there’s almost 3 seconds of delay. When you type 30 seconds, this delay is growing to about 7 seconds. That’s crazy, isn’t it?

So I started searching, and searching, and testing, until I finally found out what was the problem. If you want to find out yourself what is the cause, that makes AS3 Timer class functioning so badly, check out another sample application I’ve published below.
The only thing this application’s doing, is printing down the numbers from the “count” number (text input value) until it reaches zero. The most crucial part is to understand what this “count” value is. Well, actually it’s one of the two parameters we should set before launching Timer class instance. First one (“count”) is the Timer.repeatCount property, which tells the Timer how many times is should be launched until it will stop running (and it will dispatch TimerEvent.TIMER_COMPLETE event). The second (“Delay”) is responsible for the Timer.delay parameter. It tells our Timer class instance, how often it should dispatch the TimerEvent.Timer event. This event allows us to run some tasks at specified time intervals – that’s how I’ve been counting down to zero in my countdown clock.

Remember, that delay is a milliseconds value. So for example, if you’ll set it at 1000, the TimerEvent.Timer event will be dispatched every second. If you set it at 100, it’ll be dispatched 10 times per second. And apparently, that’s the main reason that causes Flex timer to work so inaccurate. Check out my app and you’ll see. If you’ll set the delay value at 1000, and count at 10, everything should be working almost great – the timer should be running for about ten seconds. But let’s try setting for example: count = 100, delay = 100. The timer should also be running for 10 seconds (100 * 100), but check out the time differences after it finishes it’s job. Surprising, isn’t it :) . So, try setting another example: count = 1000 and delay = 10 – this is the killing one, and apparently it still should be a 10 seconds countdown!

Here’s my timer-testing application.

In other words, as you can see, the smallest Timer.delay interval you set, the more inaccurate it becomes! I don’t know exactly why it is working that way. It seems that Flash Player needs a lot of time to dispatch or catch Timer events. So, if you want your Timer to work pretty much accurate, don’t use delays smaller than 1000 milliseconds.

But what about the situations when we need to dispatch an event more often? You need a solution? So did I at some point. But even though I was trying very hard, I couldn’t fix this Timer problem (if you can, write it down in comment please). So I’ve decided to walk around the problem – maybe it’ll be helpful for you too. Instead of using the Timer class for my countdown clock, I’ve tried to build it basing on the Event.Enter_Frame event. And here’s the same clock, but this time it’s not using the Timer class at all, but Enter_Frame event instead.

And here’s the source code of this “fixed” countdown clock application.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="358" height="202"  viewSourceURL="srcview/index.html">
	<mx:Label x="10" y="36" fontSize="22" fontWeight="bold" width="300" height="43" id="timerLabel"/>
	<mx:Label x="10" y="99" text="Seconds" id="secondsLabel"/>
	<mx:TextInput text="10" x="69" y="97" id="timerDuration"/>
	<mx:Button x="237" y="97" label="Go" id="goButton" click="this.restartTimer(int(timerDuration.text))"/>
	<mx:Label x="10" y="141" id="startTime" width="338"/>
	<mx:Label x="10" y="167" width="338" id="finishTime"/>
	<mx:Label x="10" y="10" text="Fixed countdown clock (based on EnterFrame event)" fontWeight="bold" fontStyle="italic"/>

	<mx:Script >
		<![CDATA[
		 private var myTimer:Timer = new Timer(100);
		 private var counts:int;
		 private var maxCounts:int;
		 private var mySeconds:int;
		 private var myMiliseconds:int;

		 private function restartTimer(duration:int):void
		 {
		  this.maxCounts = duration * 25; // From the top of my head, EnterFrame event is being dispatched 25 times per second
		  this.counts = 0;
		  this.mySeconds = duration;
		  this.myMiliseconds = 100;

		  if ( this.mySeconds < 10 ) this.timerLabel.text = "0"+this.mySeconds+":00";
		  else this.timerLabel.text = ""+this.mySeconds+":00";	

		  var myDate:Date = new Date();
		  this.startTime.text = "Start time (hour/min/sec/mili) - "+(myDate.hours+":"+myDate.minutes+":"+myDate.seconds+":"+myDate.milliseconds);
		  this.finishTime.text = "";

		  this.addEventListener(Event.ENTER_FRAME,enterFrameHandler);
		 }

		 private function enterFrameHandler(event:Event):void
		 {
		  if ( this.counts < this.maxCounts )
		  {
		  if ( this.myMiliseconds == 100 )
		   {
		    this.mySeconds--;
		   }

		  this.myMiliseconds -= 4;

		  if ( this.myMiliseconds >= 10 )
		  {
		   if ( this.mySeconds < 10 ) this.timerLabel.text = "0"+this.mySeconds+":"+this.myMiliseconds;
		   else this.timerLabel.text = ""+this.mySeconds+":"+this.myMiliseconds;
		  }
		  else
		  if ( this.myMiliseconds != 0 )
		  {
		   if ( this.mySeconds < 10 ) this.timerLabel.text = "0"+this.mySeconds+":0"+this.myMiliseconds;
		   else this.timerLabel.text = ""+this.mySeconds+":0"+this.myMiliseconds;
		  }
		  else
		  {
		   if ( this.mySeconds < 10 ) this.timerLabel.text = "0"+this.mySeconds+":00";
		   else this.timerLabel.text = ""+this.mySeconds+":00";
		  }

		  if ( this.myMiliseconds < 4 ) this.myMiliseconds = 100;
		  this.counts++;
		  }
		  else
		  {
		   var myDate:Date = new Date();
		   this.finishTime.text = "Finish time (hour/min/sec/mili)- "+(myDate.hours+":"+myDate.minutes+":"+myDate.seconds+":"+myDate.milliseconds);
		   this.removeEventListener(Event.ENTER_FRAME,enterFrameHandler);
		  }
         }
		]]>
	</mx:Script>
</mx:Application>

It’s still not a perfect solution, but delays are finally much smaller. That made my clock working more accurate than this previous one, which was based on the Timer class. Although, this solution still has some disadvantages itself – Enter_Frame event is being dispatched 25 times per second, which gives us another limitation in some situations. But anyway, in most cases it should be much more useful than this corrupted Timer class. And even though it’s not a perfect solution, it’s good to be aware, that the AS3 Timer class has some very serious limitations, and you should keep in mind these findings when setting up your timer’s delay property. Otherwise, if you’ll set up too many timers with too big delays, they could slow down your application a lot.

  1. #1 by Wojciech Ptak at June 15th, 2009

    Hi all,

    Timer class is really making our lives miserable :) Some projects just can’t be prepared without them – and as such – sometimes solutions are really hard to developed. The only hope – some update to Flash player will solve this.

    From my perspective, your solution is doing it’s work, however it’s not very usefull for enterprise and business applications. Adding listener for entering every frame is one of the first killing features to the Flash Player. Those apps like to grow really big and as such this will make them only slower :)

    However – good point, only few people realise how crappy this mechanism is.

    Cheers,
    W.

  2. #2 by bachor at October 6th, 2009

    1′ Less ticks = more precise timer
    2′ For precise timers synchronize it with time and use timer ticks just for updating time value.

  3. #4 by Brody at April 10th, 2013

    Where’s the nearest cash machine? buy topamax online processed through the eligibility edits. If the claim passes all other editing and you have

  4. #5 by viagra at July 14th, 2014

    Does your site have a contact page? I’m having a tough time locating

  5. #6 by พิเศษ at August 5th, 2014

    Very shortly this web page will be famous
    among all blogging and site-building viewers, due to it’s pleasant articles

  6. #7 by visit at October 25th, 2014

    hi!,I love your writing so much! proportion we keep up a correspondence extra about your article on AOL? I require an expert on this space to solve my problem. May be that’s you! Having a look ahead to peer you.

  7. #8 by Rueben at November 6th, 2014

    Thanks for another magnificent post. The place else may just anybody get
    that type oof info in such an ideal means of
    writing? I have a presentation next week, and I am at the look for such info.

  8. #9 by ugg ダコダ at November 9th, 2014

    変な奇数うわーそれはあった。 長いコメントが、私は表示私のコメントを提出した後にクリックした私はちょうど書きました。気まぐれ…よく私は再び上ですべてのことを書いていないよ。 とにかくブログ、単にを言いたかった!
    ugg ダコダ http://www.sorepa.cl/fotos_noticias/UGG/20141105170723-44ypx5.html

  9. #10 by 財布 プラダ 人気 at November 9th, 2014

    オンラインのそれほどではない読者は正直に言うとが、あなたのサイト本当に素晴らしい、それを維持する!私が先に行くと、あなたのブックマークう|後で戻ってきてサイトサイトを。 すべてのベスト
    財布 プラダ 人気 http://www.myincube8r.com/software/prada/20141107183719-02ev.html

  10. #11 by sac a dos lancel at November 10th, 2014

    Room Tip: Four poster rooms are enormous

  11. #12 by porno at March 9th, 2015

    You really make it appear really easy with your presentation but I in finding this matter to be really one thing that I think I would never understand. It sort of feels too complex and extremely huge for me. I am taking a look ahead on your subsequent put up, I will attempt to get the dangle of it!

  12. #13 by 7m at July 13th, 2015

    Tip: vaobong fastest

  13. #14 by ibet888 at July 23rd, 2015

    It’s great, thank you for the article or

  14. #15 by ibet888 at July 24th, 2015

    Who can help me create pages like this are not

  15. #16 by salvation diet at November 6th, 2015

    Chew your food properly and eat it off a pretty
    plate. eval(ez_write_tag([[300,250],’brighthub_com-medrectangle-1′]));.
    Therefore, wandering around both in the office and outside helps to lose
    pound, give a mental break from the daily grind as well
    as reduce stress level.

  16. #17 by Countdown Timer Alarm at November 23rd, 2015

    Multiple Countdown Timers change e’er been an galvanizing Countdown Timer Alarm break of any circumstance.
    Reckoning backwards to see how much quantify is leftish before an event increases the anxiousness of the prevision. It
    would be extraordinary if you had the opportunity to create
    your rattling own Ajax Countdown Timer. A dolabrate
    to use programme you would enjoy having. By simply adding
    a widget you could enjoy the Date Countdown Timer on your concealment.

  17. #18 by Investasi Emas at December 7th, 2015

    The log you keep needs to be very accurate because
    the IRS will check this log againnst bills and receipts.
    ” The fact is, writers and reporters are always on the lookout for “sources,” especially at the local level. As things progress, expect the structure to change.

  18. #19 by Grosirbajuku at December 7th, 2015

    The best tip around this iis too spread your Giirl
    Wars Online Friend Codes as much as you possibly can. Brands have leveraged on the power of
    online shopping and have their brand websites that allows the user to shop
    online with safety and ease. All you need to do iss click
    the button and the Pandora box opens.

  19. #20 by バイク王 売る at August 5th, 2016

    バイクを高く売るならこちら。

  20. #21 by custom jerseys nfl at July 13th, 2017

    I am cheap Drew Kaser jerseys supplier online, take coupon code here: custom jerseys nfl

(will not be published)
  1. No trackbacks yet.