jvhang
jvhang

Reputation: 777

How to inspect the Javascript setTimeout queue?

I have a very complex JS application that queues a lot of logic to be executed when possible via setTimeout(). This is necessary to prevent slow script warnings in IE7/8.

I know. I wish I didn't have to support these either.

Anyhow, I need to know when all of these operations are finished so I can unlock the UI for the user.

Is there a way to inspect the JS task queue?

Upvotes: 1

Views: 2594

Answers (4)

Ben in CA
Ben in CA

Reputation: 851

There used to be several tools for inspecting running intervals; such as:

https://chrome.google.com/webstore/detail/setinterval-sniffer/odkgihncjgklajjabihpoipfnpcjhdno/related?hl=en

https://chrome.google.com/webstore/detail/timeouts-and-intervals/oagmfffelnnohpbidjknmhfangoahcan

But it appears these don't work anymore. If anyone knows of a way of seeing these in Chrome (or FireFox / Edge) Developer Tools, please comment.

Upvotes: 0

Trentium
Trentium

Reputation: 3719

Realize this is a very dated thread, but ran into a similar need to queue tasks, with the option to clear redundant tasks in the javascript setTimeout queue, as the process queue begins emptying...

Essentially wrapped the setTimeout and clearTimeout functions to track the setTimeout process queue using a Map (named setTimeoutQueue). (This function wrapping technique isn't anything new, as I've seen it employed with custom event handler mixins, which preempt and postempt(?) methods of objects or classes.) The core concept being that as the processes pop off the queue, the active process can search setTimeoutQueue to determine if redundant tasks are sitting in the queue, and if so, use the preempted clearTimeout function to clear both the actual queued process and the corresponding task entry tracked by setTimeoutQueue.

const actualSetTimeout = setTimeout;
const actualClearTimeout = clearTimeout;

setTimeoutQueue = new Map();

setTimeout = function(f, ms, ...fArgs) {

  console.log(`setTimeout "${f}" - BEFORE QUEUING TASK.`);
  console.log(setTimeoutQueue);

  const setTimeoutHandle = actualSetTimeout(
    function() {
      console.log(`setTimeout "${f}" - BEFORE EXECUTION.`);
      console.log(setTimeoutQueue);

      const fResult = f(...fArgs);
      setTimeoutQueue.delete(setTimeoutHandle);

      console.log(`setTimeout "${f}" - AFTER EXECUTION.`);
      console.log(setTimeoutQueue);
      return fResult;
    },
    ms,
    ...fArgs
  );

  setTimeoutQueue.set(setTimeoutHandle, [f, ms, ...fArgs]);

  console.log(`setTimeout "${f}" - AFTER QUEUING TASK.`);
  console.log(setTimeoutQueue);

  return setTimeoutHandle;
}

clearTimeout = function(id) {
  actualClearTimeout(id);
  setTimeoutQueue.delete(id);
}

console.log(setTimeoutQueue);
setTimeout((...args)=>{console.log(`5 sec timer fired with params = ${args}`)}, 5000, 555, 5555);
setTimeout((x)=>{console.log(`2 sec timer fired with first param = ${x}`)}, 2000, 222);
setTimeout(()=>{console.log(`Queued for immediate execution.`)});
console.log(setTimeoutQueue);

Obviously all the "console.logs" are there to assist in understanding the execution sequence, in addition to denoting custom code injection points. Only uncertainty I have is any side effects of preempting the setTimeout and clearTimeout functions, but time and constructive criticism will tell... ;-)

Upvotes: 1

Andrew Vermie
Andrew Vermie

Reputation: 633

I don't think you can do that in any browser.

But you can use the Promise pattern to notify someone when an asynchronous operation completes.

Here is an simplistic example using jQuery's promise implementation, using setTimeout to breakup execution so the user won't be warned about scripts that take too long to execute:

// a factory for promises
function createPromiseFor(doWork) {
    // create a deferred object
    var deferred = $.Deferred();

    // a function that will schedule our work;
    function scheduleMoreWork() {
        setTimeout(function () {

            // doWork does at least some of the work, and tells us when it is done
            var finished = doWork();

            if (finished) {
                deferred.resolve();
            } else {
                scheduleMoreWork();
            }
        }, 1);
    };

    // schedule the first one
    scheduleMoreWork();

    return deferred.promise();
}

// assuming ourFunction does its work in chunks and can resume from where it left off...
var promise = createPromiseFor(ourFunction);
promise.done(function () {
    alert('done!');
});

And here is an example of a function which can pause itself, and resumes the next time it is called:

var fn = (function () {
    var goal = 100,
        iterationsPerInvocation = 10,
        current = 0;

    function invoke() {
        for (var i = 0; i < iterationsPerInvocation; ++i) {
            current++;
        }
    }

    return function () {
        invoke();
        return current === goal;
    }
})();

var promise = createPromiseFor(fn);
promise.done(function () {
    alert('done!');
});

Upvotes: 1

adv12
adv12

Reputation: 8551

You could keep a count of how many chunks you've started and have each chunk decrement that count, check whether it's zero, and if so run your completion logic.

Upvotes: 1

Related Questions