Timers with Cleared, Pending, Executed, & Paused States Primitives If you have spent any time at all writing JavaScript, you are likely to be well acquainted with some of the most ancient functionality built into the language, namely and . setTimeout() clearTimeout() If you somehow are unfamiliar with JS timers, allows you to schedule a delay in milliseconds after which time it will trigger execution of a callback function (or other arbitrary chunk of code, though for the purposes of this article, we will focus on callbacks). It returns a unique timer identifier that allows you the ability to cancel the timeout before it has triggered by passing it to . setTimeout() clearTimeout() function greetWorld() {alert('Hello, world!');} var timerId = setTimeout(greetWorld, 2000); This is about as simplistic of an example as possible. Two seconds after the statement is executed, the callback is run. setTimeout() greetWorld() Before those two seconds have elapsed, you may cancel the pending call by calling . Other than setting and clearing a timeout, there is no other useful functionality provided. clearTimeout(timerId) Limitations When you are coding for straightforward requirements, the built-in functionality may suffice, however its limitations can leave much to be desired. Specifically, there are some unanswerable questions you may answered: need Has a timeout been created for my callback? Is the execution of my timeout’s callback still pending? Has my timeout’s callback been executed yet? Typically, if a developer needs any of these questions answered in their project, they may write some task-specific utility logic to help achieve that end. My approach was to create a generalized solution that would provide the missing functionality enumerated here, without hindering or complicating the ease of use and features provided by the built-in primitives. Timeout.js _Timeout - Interactive, stateful JS (ES6) timeout interface_github.com rommelsantor/Timeout npm install smart-timeout The object is an interactive, stateful interface that seeks to accomplish the goals described above. Using the revealing module pattern, it exposes the following functions: Timeout Timeout.set(callback, delay = 0, param1, param2, ...) Timeout.set(customId, callback, delay = 0, param1, param2, ...) Timeout.clear(key, delete = true) Timeout.exists(key) Timeout.pending(key) Timeout.remaining(key) Timeout.executed(key) Timeout.pause(key) Timeout.paused(key) Timeout.resume(key) Timeout.restart(key) When setting a new timeout, you may optionally define a custom string identifier ( ) by which to uniquely identify it. If this parameter is omitted, then itself will act as the timeout’s unique identifier. In each function described as accepting as its parameter, represents either the or , whichever was used in the corresponding call to . customId callback key key customId callback Timeout.set() returns a function that when executed returns a boolean indicating whether or not the delay has elapsed and the callback has been triggered. It is equivalent to calling . If the same identifier is repeated in a call to , the former will be cleared before the latter is added. (If you intentionally want to set multiple concurrent timeouts for the same callback, just use a distinct for each.) Timeout.set() Timeout.executed() Timeout.set() customId will simply clear the timeout associated with the specified (if there is such a timeout) and erase any evidence that the timeout ever existed. It returns no value. Timeout.clear() key returns true if a timeout has been set and not cleared for the specified , regardless of whether or not its has elapsed. Timeout.exists() key delay returns true if a timeout exists for the specified and its has not yet elapsed (i.e., its has not yet been triggered). Timeout.pending() key delay callback [added in v2] returns the milliseconds remaining in the countdown until execution. Timeout.remaining() returns true if a timeout exists for the specified and its has elapsed (i.e., its has been triggered). Timeout.executed() key delay callback [added in v2] allows you to pause the countdown for a timer that has not yet executed. Timeout.pause() [added in v2] returns true if a pending timeout is currently paused. Timeout.paused() [added in v2] allows you to resume the countdown for a paused timer. Timeout.resume() [added in v2] allows you to restart the countdown for a pending or paused timer with the original time. Timeout.restart() delay Basic Functionality Considering the very simplistic example callback provided earlier, the most basic functionality is demonstrated here. (Note that the remainder of the code examples in this article will use ES6.) const didGreet = Timeout.set(greetWorld, 2000) if (Timeout.exists(greetWorld)) {// trueconsole.log('greeting has been scheduled')} if (Timeout.pending(greetWorld)) {// trueconsole.log('greeting is waiting to be issued')} // ...wait for 2 seconds to elapse... if (didGreet()) {// trueconsole.log('the greeting was issued')} // ^that is identical to calling this:if (Timeout.executed(greetWorld)) {// trueconsole.log('as I said, the greeting was issued')} Timeout.pending(greetWorld) // false - it ran Timeout.exists(greetWorld) // true - it still exists Timeout.clear(greetWorld) Timeout.exists(greetWorld) // false - it has been cleared Instead of using the callback as the unique key, you may alternately specify a custom identifier: const didGreet = Timeout.set('myGreeting', greetWorld, 2000) if (Timeout.exists('myGreeting')) {// trueconsole.log('greeting has been scheduled')} // etc. See it in Action Throttling Example The basic usage by itself is helpful, but let us consider a more complicated use case: throttling excessive window events. In this case, we will not be using a delay to trigger a timeout callback, but merely as a time tracker to test whether or not the specified delay has elapsed. Let’s say our requirements dictate that we add class to the element whenever the window is scrolled down any distance from the top of the page. The problem is that when the window is scrolling, a flood of events are triggered, and we do not want to bog down the page by reacting to each one. This could be accomplished with a throttle function from an external library like lodash, but for this example, we’ll write it ourselves using to restrict the callback so it may execute only periodically. is-scrolled <html> scroll Timeout onScroll const throttle = (delay, callback) => (...args) => !Timeout.pending(callback) && Timeout.set(callback, () => {}, delay) ? callback.apply(this, args) : null const onScroll = () => { const isScrolled = $(window).scrollTop() > 0 $('html').toggleClass('is-scrolled', isScrolled)} const onScrollThrottled = throttle(100, onScroll)$(window).scroll(onScrollThrottled) The function accepts as its two parameters the delay in milliseconds and the callback to execute if the delay has elapsed since the last time it was executed. It returns a function appropriate for use as an event callback. throttle() at least This is obviously a very specific (albeit a little convoluted) use of the object, but it demonstrates the flexibility it provides with succinct, clear operations that are not possible with and alone. Timeout setTimeout() clearTimeout() Conclusion There may be other libraries out there that provide similar functionality, however, if so they must be well hidden because I have not been able to find them, which is why I decided to write this post. I hope it proves as useful to you as I hope it can be!