46

So I was wondering, is there any feasible way in JavaScript to view information about scheduled timeouts and intervals that you don't explicitly know about (I know setTimeout and setInterval return a handle that can be used to refer to the scheduled instance, but say that this is unavailable for one reason or another)? For instance, is there a way to use a tool like Chrome's JavaScript console to determine what timeouts are currently active on an arbitrary page, when they will fire, and what code will be executed when they fire? More specifically, say a page has just executed the following JavaScript:

setTimeout("alert('test');", 30000);

Is there some code I can execute at this point that will tell me that the browser will execute alert('test'); 30 seconds from now?

It seems like there theoretically should be some way to get this information since pretty much everything in JavaScript is exposed as a publicly accessible property if you know where to look, but I can't recall an instance of ever doing this myself or seeing it done by someone else.

aroth
  • 54,026
  • 20
  • 135
  • 176
  • It sounds like the sort of thing that there ought to be a Firebug plug-in for. – Spudley Apr 28 '11 at 12:22
  • Possible duplicate of [Viewing all the timouts/intervals in javascript?](http://stackoverflow.com/questions/858619/viewing-all-the-timouts-intervals-in-javascript) – Áxel Costas Pena Aug 08 '16 at 08:23

4 Answers4

19

how about simply rewriting the setTimeout function to sort of inject custom logging functionality?

like

var oldTimeout = setTimeout;
window.setTimeout = function(callback, timeout) {
  console.log("timeout started");
  return oldTimeout(function() {
    console.log('timeout finished');
    callback();
  }, timeout);
}

might work?

Quentin Pradet
  • 4,691
  • 2
  • 29
  • 41
sharp johnny
  • 814
  • 6
  • 16
  • 3
    Yes, that is not a bad alternative. But it won't provide a way to see any information about already-scheduled timeouts. – aroth Apr 29 '11 at 12:22
  • 2
    Well, if you can ensure this script is the first to run, eg put it on top of all others, then this should work, unless you want to hack into some browser internal timeouts or whatever :D – sharp johnny May 18 '11 at 06:38
  • 2
    While this would work IF it runs first... it really doesn't answer the question – Daniel Sellers Mar 29 '12 at 15:12
  • 4
    You want to make sure to return the old timeout function, so that you get the timeoutID – NotSimon Apr 16 '13 at 17:57
8

No, even the HTML5 spec (which is a rationalisation of the HTML 4.01 behaviour in current browsers, with additional features) doesn't specify a way to list the available callbacks.

Gareth
  • 133,157
  • 36
  • 148
  • 157
  • 9
    True, but the spec does seem to say that the timeout must have a corresponding `Task` which must be placed inside of a `Queue` on the current `Event-Loop`, and that "each task that is queued onto a task queue of an event loop defined by this specification is associated with a `Document`". So although the specifics of how this is to be done and where things should reside are not explicit, if the `Document` is accessible via JavaScript then it seems to follow that the `Task` should be as well by virtue of its "association" with the `Document`. Though perhaps I am interpreting it wrong? – aroth Apr 29 '11 at 12:01
3

We've just published a package solving this exact issue.

npm install time-events-manager

With that, you can view them via timeoutCollection & intervalCollection objects.

Bar Goldinfeld
  • 183
  • 1
  • 2
  • 12
  • 1
    This assumes that the **only** way to create a task is with setTimeout, and it assumes that there are not any tasks that exist **before** your package is loaded (and your decorator is added to the setTimeout). What about tasks that are created by async/await? I think your package will ignore them. – John Henckel Oct 07 '20 at 14:32
3

You could also create a timer manager module which will keep track of current timers and allow you to get, add, stop and stop all timers.

var timers = (function() {
  //
  var timers = []

  //
  const getIndex = (array, attr, value) => {
      for (let i = 0; i < array.length; i += 1) {
          if (array[i][attr] === value) {
              return i
          }
      }
      return -1
  };

  // add
  const add = (callback, time) => {
    var id = setTimeout(() => {
      let index = getIndex(timers, 'id', id)
      timers.splice(index, 1)
      callback()
    }, time)
    timers.push({
      id: id,
      time: time,
      debug: callback.toString()
    })
  };

  // get all active timers
  const all = () => timers

  // stop timer by timer id
  const stop = (id) => {
    if (!isNaN(id)) {
      let index = getIndex(timers, 'id', id)
      if (index !== -1) {
        clearTimeout(timers[index].id)
        timers.splice(index, 1)
      }
    }
  };

  // stop all timers
  const stopAll = () => {
    for (let i = 0; i < timers.length; i++) {
      clearTimeout(timers[i].id)
    }
    timers = []
  };

  return {
    add: add,
    all: all,
    stop: stop,
    stopAll: stopAll,
  };
})();

//
timers.add(function() {
  console.log("timeout 1 fired");
}, 1000)

timers.add(function() {
  console.log("timeout 2 wont get fired");
}, 2000)

timers.add(function() {
  console.log("timeout 3 fired");
}, 3000)

timers.add(function() {
  console.log("timeout 4 fired, timers", timers.all());
}, 4000)

timers.add(function() {
  console.log("timeout 5 fired");
}, 5000)

console.log('All timers', timers.all())

console.log("kill timer 2")
timers.stop(2)

Run the snippet to see it in action.

Lawrence Cherone
  • 46,049
  • 7
  • 62
  • 106