Timing service

Every once in a while, I find myself in need of a timing mechanism. Something that will handle timing and scheduling, and allow me to hook with a custom functionality every interval.

Some uses can be, for example, a clock counting down or simply showing the time passing by, polling the server back-end for new data, etc. Basically, anything that needs to happen every given interval.

Sure, you could implement a timer of sorts where applicable or even angular’s own $interval, but that would be terrible code duplication not to mention a maintenance overhead.

So, I came up with a different approach.
I decided to implement a timing service to centralize this timing mechanism.

Other entities will register to it and provide a handler function and an interval and the timing service will handle the interval granularity and be in-charge of calling the handler function when the interval is up.

Typically, registrants would be other services, but since AngularJS relies on composition via injection it could just as easily be a directive or a controller.

So, first thing this service will need is somewhere to store the registrants configuration – the registrants map.
It will also need to know it’s own interval – internalInterval.

timingSrv-1

 

I set the interval hard-coded to 1 second but it could easily be set from a configuration file or something, as well as be any value that makes sense in your application.

Note – if you go below 1 second, though, be sure to pick a value that rounds to a complete second every few intervals to allow registrants the option to have a complete second interval (i.e. 250ms interval will reach 1 second every 4 intervals, as opposed to 400ms which will miss the 1 second interval).

Next step – providing registrants the means to register and unregister:

timingSrv-2

 

So, registrants will use the register method to register by providing:

  • id – a unique identifier. A good candidate is the registrant name (service, directive or controller name is unique).
  • tickHandler – a callback Function reference that contains the registrant’s logic to be executed each interval
  • interval – the interval (in ms)
  • delay – how much time (in ms) should pass before the first launch. Obviously, passing 0 will cause it to tick on next internalInterval cycle.

Respectfully, registrants will use the unregister method to unregister. It is enough to provide the id when unregistering.

Next, we need the logic for the tick:

timingSrv-3

 

So every tick we go over all the registrants and for each, we call it’s tick callback handler if it’s delay is <= 0.

Now, all this is great, but it’s not enough. All we have implemented up until now is just the API for registrants to use.
We still need to make the service run every internalInterval.

So here is the final step:

timingSrv-5

 

So, we added the $interval service as dependency and then added an internal start method that is called only once just before the service is returned. It simply fires up the $interval to tick every internalInterval and execute the tick method. Since $interval will only tick on next interval, we call the tick method synthetically once.

This service can be treated as a Timing service or it can be extended to behave as a Scheduler or even a Task Manager, if you add support for the number of times the task (registrant) should run in total or at a specific date/time.

The complete code can be found on Github.
Feel free to use and abuse 🙂
If you modify jsBlackBelt code, please let me know or submit a pull request for the benefit of others.

 

7 thoughts on “Timing service

  1. Nice one!

    We use this as well. But without callbacks with promise that calls notify each tick and then the “customers” get the tick. And hide the unregister, we pass the scope and listen to scope.$on(‘$destroy’… and remove the “customer” from the registered clients of the tick service.

    Thanks!

    Liked by 1 person

    • Hi Alex, thanks for the comment!

      I can relate to the promise instead of callback approach, though I’m not sure it’s as robust.

      Promises are one time in nature, which requires the registrant to re-register every time the promise resolves, whereas the callback approach only requires the registrant to register once and that’s it.

      Regarding hiding the unregister method and using $scope to listen on $destroy, this is something I don’t like much. It sounds broken to me that the service be aware of the registrant’s $scope. It is, for lack of a better term, out of the timingSrv’s scope 🙂

      Besides, I’d rather have the registrant decide when it wants to unregister, be it because of $destroy, some business logic or something else entirely, and let the timing service know about it when it does.

      Sefi

      Like

  2. Hi do you have sample code of you to use this functionality. I am new with angular and am trying to use this implement an interface that has a clock and a list with updates based items based on if the duration between a start time and end time becomes zero

    Like

    • Hmm… I was sure I also committed a test along with the service…
      Just checked the github repo and saw there isn’t a test for it.

      I’ll try to commit a test in the near future that you can also use as a reference on how to use it.

      Basically, the service is responsible of starting itself, so all you need to do (once you import the module into your app) is register to it using the register(id, tickHandler, interval, delay) method. Consult the comments inline for how to register.

      Don’t forget to unregister to the service using the unregister method (for example when the url changes) so that you wont have any background calls that should not be happening…

      Like

Leave a comment