In my AngularJS applications, I want my controllers to respond to events. But, often times, I don't necessarily want the controller to react to every single event; rather, I'd like to throttle those events such that closely timed events trigger only one reaction. After implementing this logic a good number of times, I wanted to see if I could encapsulate it inside of a reusable timer that could be injected into any of my AngularJS classes.
The pattern that I'm talking about looks a bit like this:
In this case, when an event occurs, we clear any existing timeout and then re-create it. Basically, I'm "debouncing" the event such that the callback is only invoked at the end of a group of closely timed events. This is the logic that I'd like to simplify.
In AngularJS, we have the $timeout() service, which is already hooked into the $rootScope and the $digest lifecycle. So, rather than trying to re-implement that, I'd like to create a thin wrapper class that uses $timeout internally. This way, my logic is isolated around the actual timing and not around the error handling and digest invocation.
What I've come up with only had a few public methods:
- isActive() - Determines if the timer is currently counting-down.
- start() - Starts the timer.
- stop() - Stops the timer (if its active).
- restart() - Resets the timer and starts it again.
- teardown() - Cleans up object references for garbage collection.
Mostly, what I wanted was the start() and restart() methods, which mimic the code pattern that I outlined above:
As you can see, I have a timer that debounces click events for 2-seconds. And, even though the callback is happening asynchronously, AngularJS still knows about it since the encapsulated $timeout() service is triggering a digest.
Not much else to say, really. This just a fun experiment.
Want to use code from this post? Check out the license.