Hi I have a script that cycles through an array of strings and displays them on a specific class using innerHTML.
The arrays setup looks something like:
var myArray = [
"TextString1",
"TextString2",
"TextString3",
"TextString4"
]
var AnotherArray = [
"AnotherArray1",
"AnotherArray2",
"AnotherArray3",
"AnotherArray4"
]
And the SetInterval function itself looks like this
function ArrayPlusDelay(array, delegate, delay) {
var i = 0
// seed first call and store interval (to clear later)
var interval = setInterval(function() {
// each loop, call passed in function
delegate(array[i]);
// increment, and if we're past array, clear interval
if(i++ >= array.length - 1) {
i = 0 //clearInterval(interval);
}
}, delay)
return interval
}
ArrayPlusDelay(myArray, function(obj) {
document.querySelector('.cycle_questions').innerHTML = obj
},2000)
ArrayPlusDelay(AnotherArray, function(obj) {
document.querySelector('.cycle_research').innerHTML = obj
},2000)
Where do I inject the AddEventListener to make the looping stop? And on mouseleave make make the loop continue?
Thanks!
Here is the solution.Here i've introduced two different array.For the purpose of segregation you need two separate interval object.First is intervalObjOne and second is intervalObjTwo.Two object for two different array.If want to use a third array create a third one.You can even make it dynamic,but for the purpose of simplicity i kept them static.
domSelectorByClass and domSelectorByQuery is just a shorter version for document.GetElemensByClassName and document.querySelector to keep the codebase clean.
then the intervalArr is introduced which contain both intervalObjOne and intervalObjTwo.
Then run a forEach loop to iterate over the intervalArr and call attachInterval,addMouseLeaveEvent and addMouseOverEvent functions and pass respective intervalObj to each of them as parameter.
attachInterval event will initiate a setInterval on respective intervalObj's interval property.
addMouseLeaveEvent function will attach a mouseleave event on the respective div element.
addMouseOverEvent function will attach a mouseover event on respective div which will call clearInterval to stop the setInterval.
var myArray = ["TextString1","TextString2","TextString3","TextString4"]
var anotherArray = ["TextString5","TextString6"]
var intervalObjOne = {array: myArray,interval : 'undefined',tick:0,className:'cycle_questions'}
var intervalObjTwo = {array: anotherArray,interval: 'undefined',tick:0,className:'cycle_research'}
var domSelectorByClass = document.getElementsByClassName.bind(document)
var domSelectorByQuery = document.querySelector.bind(document)
var intervalArr = [intervalObjOne,intervalObjTwo]
function ArrayPlusDelay(delegate, delay,intervalObj) {
var interval = setInterval(function() {
delegate(intervalObj.array[intervalObj.tick])
if(intervalObj.tick++ >= (intervalObj.array).length - 1) {
intervalObj.tick=0
}
}, delay)
return interval
}
intervalArr.forEach(function(elem,index){
attachInterval(elem)
addMouseLeaveEvent(elem)
addMouseOverEvent(elem)
})
function attachInterval(intervalObj){
intervalObj.interval = ArrayPlusDelay(function(obj) {
domSelectorByQuery('.'+intervalObj.className).innerHTML = obj
},2000,intervalObj)
}
function addMouseOverEvent(intervalObj){
domSelectorByClass(intervalObj.className)[0].addEventListener('mouseover',function(event){
clearInterval(intervalObj.interval);
});
}
function addMouseLeaveEvent(intervalObj){
domSelectorByClass(intervalObj.className)[0].addEventListener('mouseleave',function(event){
attachInterval(intervalObj)
});
}
<div class='cycle_questions' style='display:inline-block;border:1px solid red;width:100px;height:40px;'></div>
<div class='cycle_research' style='display:inline-block;border:1px solid green;width:100px;height:40px;'></div>
What you can do here is:
1. having an object which store your state
2. Generate a scope to store the position of i:
I slighly modified your code to have something easyer to test:
var myArray = [
"TextString1",
"TextString2",
"TextString3",
"TextString4"
];
var myArrayFoo = [
"TFoong1",
"TFoong2",
"TFoong3",
"TFoong4"
]
// Used to generate your scope
var myFnGenerator = function(array, delegate, interval) {
var i = 0;
return function() {
if (interval.stop) {
return false;
}
delegate(array[i]);
if(i++ >= array.length - 1) {
i=0
}
};
}
function ArrayPlusDelay(array, delegate, delay) {
// The object which store your state
var interval = {
stop: true
};
var fn = myFnGenerator(array, delegate, interval);
interval.timeout = setInterval(fn, delay);
interval.stop = false;
return interval;
}
var interval = ArrayPlusDelay(myArray, function(obj) {
console.log(obj);
},2000)
var intervalFoo = ArrayPlusDelay(myArrayFoo, function(obj) {
console.log(obj);
},2000)
document.getElementById('foo').addEventListener('mouseenter',function(event){
interval.stop = true;
});
document.getElementById('foo').addEventListener('mouseout',function(event){
interval.stop = false;
});
document.getElementById('foo').addEventListener('mouseenter',function(event){
intervalFoo.stop = true;
});
document.getElementById('foo').addEventListener('mouseout',function(event){
intervalFoo.stop = false;
});
I think this way is easyer to read than clearTimeout then relaunch it inside mouseout (less copy paste), but I guess it is more a code's style. If you prefer having clearTimeout:
var myArray = [
"TextString1",
"TextString2",
"TextString3",
"TextString4"
];
var myArrayFoo = [
"TFoong1",
"TFoong2",
"TFoong3",
"TFoong4"
]
var myFnGenerator = function(array, delegate) {
var i = 0;
return function() {
delegate(array[i]);
if(i++ >= array.length - 1) {
i=0
}
};
}
function launchInterval(interval, delay) {
if (interval.timeout === null) {
interval.timeout = setInterval(interval.fn, delay);
}
}
function clearMyInterval(interval) {
if (interval.timeout !== null)
clearInterval(interval.timeout);
interval.timeout = null;
}
function ArrayPlusDelay(array, delegate, delay) {
var interval = {
timeout: null
};
var fn = myFnGenerator(array, delegate);
interval.fn = fn;
launchInterval(interval, delay);
return interval;
}
var interval = ArrayPlusDelay(myArray, function(obj) {
console.log(obj);
},2000)
var intervalFoo = ArrayPlusDelay(myArrayFoo, function(obj) {
console.log(obj);
},2000)
document.getElementById('foo').addEventListener('mouseenter',function(event){
clearMyInterval(interval);
});
document.getElementById('foo').addEventListener('mouseout',function(event){
launchInterval(interval, 2000);
});
document.getElementById('foo').addEventListener('mouseenter',function(event){
clearMyInterval(intervalFoo);
});
document.getElementById('foo').addEventListener('mouseout',function(event){
launchInterval(intervalFoo, 2000);
});
Must meet your needs. I let interval accessible from the function of your setInterval to be able to use clearInterval inside the callback.
Related
I'm writing a counter to count an object, and it looks like this:
function myFunc(param) {
this.param = param;
param.foo = function() {
var object = window.JSON.parse(data);
for (i in object) {
counter++;
}
}
}
var foo = new myFunc('data.json');
var counter = 0;
document.write(counter); // displays 0
How can I achieve to get the counter value outside the function? I tried almost everything, from window to return to separate functions.
Any clue?
Update
I prefer a better design like this
function myFunc(param) {
this.param = param;
param.foo = function() {
var object = window.JSON.parse(data);
var counter = 0;
for (i in object) {
counter++;
}
return counter;
}
}
var foo = new myFunc('data.json');
document.write(counter); // displays undefined
Update 2
Sorry, thought it would be easier to have a sample code. But here's the real one: https://gist.github.com/BobWassermann/e709ec303477a015b609
I think you have a couple issues here.
First, you're setting your counter to 0 just before you write. It will always be 0 no matter what you do, even with hoisting.
Second, you never call the foo function, so your counter is never incremented.
Third, param.foo isn't public. I think you want it to be this.foo = function(){ ... }.
Here's a simplified version of the code you posted with my tweaks:
var counter = 0;
var foo;
function myFunc() {
this.foo = function() {
counter = 1000;
}
}
foo = new myFunc();
foo.foo();
document.write(counter);
JSFiddle: http://jsfiddle.net/dgrundel/2ojw2332/2/
Note that JSFiddle doesn't allow document.write, so replaced that part.
function myFunc(param) {
this.param = param;
this.foo = function () {
var object = window.JSON.parse(this.param),
counter = 0,
i;
for (i in object) {
counter++;
}
return counter;
};
}
var foo = new myFunc('{"a":99}');
out(foo.foo());
function out(s) {
document.getElementById('out').innerHTML = '<pre>' + s + '</pre>';
}
<div id="out"></div>
As #Nina Scholz pointed out earlier, I'm retrieving the data asynchron. Javascript started painting the dom before all the values where loaded.
This fixed my problem:
if (document.readyState) {
setTimeout(function() {
var objLen = Object.keys(obj).length;
console.log(objLen);
}, 100);
}
I'm waiting for the document to be ready, then add an additional timeout as buffer.
//this is a loop every 10 seconds it will run again and again.
function GetSomthing(){
start = new Date().getTime()
//do someting
end = new Date().getTime()
}
TotalTime = (end - start);//Total Time reset every time the function restart execution
We will get total time taken to execute the GetSomthing function on the variable "TotalTime".
When The GetSomthing function calls again the "TotalTime" Starts Again from Zero.
I need to save "TotalTime" to a variable that shows overall the Time taken by Main Function regardless of GetSomthing Function.
Can We save
TotalTime = (end - start); to an array and sum all array values to a new variable?
I created a function for a similar question here on SO today. This one automatically calculates very accurate average execution times of global functions. You can modify it to suit your needs:
function testFunc(test) {
for(var i=0;i<100000000;i++) { i = i+test; }
};
function testFunc2(test) {
for(var i=0;i<100000000;i++) { i = i+test; }
};
var getPerfResults = (function(){
var timeRecords = {}, xTr = 0, name;
var setPerfRecords = function(oFn, fnIdentity) {
timeRecords[fnIdentity] = [];
return function(){
var xTime = performance.now();
var xResult = oFn.apply(this, arguments);
xTime = performance.now()-xTime;
timeRecords[fnIdentity].push(xTime);
return xResult;
};
};
for (name in window) {
try { window[name]; // Security exception may occur here
if (typeof window[name] === 'function')
window[name] = setPerfRecords(window[name], name);
}
catch(err) { }
}
return function() {
var resultObj = {}, n, i;
for(i in timeRecords) {
if(timeRecords.hasOwnProperty(i)
&& timeRecords[i].length > 0) {
resultObj[i] = 0;
for(n=0;n<timeRecords[i].length;n++) {
resultObj[i] = resultObj[i]+timeRecords[i][n];
}
resultObj[i] = resultObj[i]/timeRecords[i].length;
}
}
return resultObj;
};
}());
testFunc(1);
testFunc(10);
testFunc(100);
testFunc(1000);
testFunc(10000);
testFunc2(0);
document.body.innerHTML = JSON.stringify(getPerfResults());
I have a simple class below that starts and then updates a count every second. How would I go about adding functionality for it to listen for a specific value and then fire a callback?
function Counter() {
this.currentCount = 0;
}
Counter.prototype.start = function() {
setInterval(this.update, 1000);
};
Counter.prototype.when = function(value, callback) {
callback(value);
};
Counter.prototype.update = function() {
this.currentCount++;
};
In my mind it would work something like this.
var counter = new Counter();
counter.when(50, function(value) {
console.log('We arrived at ' + value + ', the requested value.');
});
counter.start();
This is just a nice homework, I'll do that for you ;) Please have a look on my solution:
function Counter() {
this.currentCount = 0;
this.conditions = [];
this.interval = undefined;
}
Counter.prototype.start = function() {
if (!this.interval) {
var that = this;
this.interval = setInterval(function () {
that.update();
}, 1000);
}
};
Counter.prototype.stop = function () {
if (this.interval) {
clearInterval(this.interval);
this.interval = undefined;
}
this.currentCount = 0;
};
Counter.prototype.when = function(value, callback) {
var that = this;
this.conditions.push(function () {
if (that.currentCount === value) {
callback.call(that, value);
}
});
};
Counter.prototype.update = function() {
this.currentCount++;
for (var i = 0, l = this.conditions.length; i < l; i++) {
var condition = this.conditions[i];
condition();
}
};
var counter = new Counter();
counter.when(50, function(value) {
console.log('We arrived at ' + value + ', the requested value.');
});
counter.when(60, function (value) {
console.log('Stop at ' + value + '!');
this.stop();
});
counter.start();
and it's fiddled!
Another answer here made a good argument in hiding private variables, but implemented it a bit too confused, so this is another way of doing it similar. Instead of shared prototype functions this is using instance functions. Some may say this needs more memory, but I don't believe it's significant, and allows to easily have privates in a real constructor function.
var Counter = function () {
var that = this, currentCount = 0,
conditions = [], interval;
var update = function () {
currentCount++;
for (var i = 0, l = conditions.length; i < l; i++) {
var condition = conditions[i];
condition();
}
};
this.start = function () {
if (!interval) {
interval = setInterval(function() {
update.call(that);
}, 1000);
}
};
this.when = function (value, callback) {
conditions.push(function () {
if (currentCount === value) {
callback.call(that, value);
}
});
};
this.stop = function () {
if (interval) {
clearInterval(interval);
interval = undefined;
}
currentCount = 0;
};
};
var counter = new Counter();
counter.when(50, function(value) {
console.log('We arrived at ' + value + ', the requested value.');
});
counter.when(60, function (value) {
console.log('Stop at ' + value + '!');
this.stop();
});
counter.start();
see it fiddled!
Notice also that in both examples, counter is instanceof Counter and Object,
whereas Counter is instanceof Function and Object (why I like to write so much code ;))
To support multiple whens:
Add an array of whens in your class:
function Counter() {
this.currentCount = 0;
this.whens = [];
}
Then let the when function push to that:
Counter.prototype.when = function(value, callback) {
this.whens.push({'time' : value, 'callback' : callback});
}
And check for these whens at update:
Counter.prototype.update = function() {
this.currentCount++;
for(var w in this.whens) {
if(this.currentCount == this.whens[w].time) {
this.whens[w].callback();
}
}
}
Try something more like:
function Counter(interval, val, func){
this.currentCount = 0;
setInterval(function(){
this.currentCount++;
if(this.currentCount === val)func();
}, interval);
}
var nc = new Counter(1000, 50, function(){
console.log('We have arrived at '+nc.currrentCount);
});
There is an argument to be made for something like this instead:
var Counter = (function() {
var update = function() {
var idx, callbacks;
this.currentCount++;
callbacks = this.callbacks[this.currentCount];
if (callbacks) {
for (idx = 0; idx < callbacks.length; idx++) {
callbacks[idx](this.currentCount);
}
}
};
var start = function() {
var counter = this;
setInterval(function() {update.call(counter)}, 1000);
};
var when = function(count, callback) {
(this.callbacks[count] || (this.callbacks[count] = [])).push(callback);
};
return function() {
var config = {currentCount: 0, callbacks: {}};
this.start = function() {return start.call(config);};
this.when = function(count, callback) {
return when.call(config, count, callback);
};
// this.stop = ... // if desired
};
}());
This is somewhat more memory intensive than the prototype-based version of the code. I wouldn't use it for something where you were expecting hundreds of thousands or millions of objects. But it has the advantage that it truly encapsulates the data you might like to keep hidden, such as currentCount and the list of callbacks. It doesn't expose an unnecessary update function. And it's not terribly more heavy than the prototype version. Each instance has its own start and when functions, but those are just thin wrappers around common functions.
It is a bit more difficult to add a stop function to this in the same manner, unless you don't mind exposing the the result of setInterval. But it is doable.
I'm writing an application that utilizes JavaScript timeouts and intervals to update the page. Is there a way to see how many intervals are setup? I want to make sure that I'm not accidentally going to kill the browser by having hundreds of intervals setup.
Is this even an issue?
I don't think there is a way to enumerate active timers, but you could override window.setTimeout and window.clearTimeout and replace them with your own implementations which do some tracking and then call the originals.
window.originalSetTimeout = window.setTimeout;
window.originalClearTimeout = window.clearTimeout;
window.activeTimers = 0;
window.setTimeout = function(func, delay) {
window.activeTimers++;
return window.originalSetTimeout(func, delay);
};
window.clearTimeout = function(timerID) {
window.activeTimers--;
window.originalClearTimeout(timerID);
};
Of course, you might not always call clearTimeout, but this would at least give you some way to track what is happening at runtime.
I made a Chrome DevTools extension that shows all intervals. Cleared ones are greyed out.
setInterval-sniffer
Instead of just have a count of timers, here is an implementation which stores all timerid's into an array. It only shows active timers while the accepted answer only counts calls to setTimeout & clearTimeout.
(function(w) {
var oldST = w.setTimeout;
var oldSI = w.setInterval;
var oldCI = w.clearInterval;
var timers = [];
w.timers = timers;
w.setTimeout = function(fn, delay) {
var id = oldST(function() {
fn && fn();
removeTimer(id);
}, delay);
timers.push(id);
return id;
};
w.setInterval = function(fn, delay) {
var id = oldSI(fn, delay);
timers.push(id);
return id;
};
w.clearInterval = function(id) {
oldCI(id);
removeTimer(id);
};
w.clearTimeout = w.clearInterval;
function removeTimer(id) {
var index = timers.indexOf(id);
if (index >= 0)
timers.splice(index, 1);
}
}(window));
This is how you can get the count of active timers on the page:
timers.length;
This is how you can remove all active timers:
for(var i = timers.length; i--;)
clearInterval(timers[i]);
Known limitations:
You can only pass a function (not a string) to setTimeout with this monkey patch.
The function assumes clearInterval and clearTimeout do the same, which they do but it could change in the future.
Seeing as Paul has only covered setTimeout I thought I would share a counter for setInterval/clearInterval.
window.originalSetInterval = window.setInterval;
window.originalClearInterval = window.clearInterval;
window.activeIntervals = 0;
window.setInterval = function (func, delay)
{
if(func && delay){
window.activeIntervals++;
}
return window.originalSetInterval(func,delay);
};
window.clearInterval = function (intervalId)
{
// JQuery sometimes hands in true which doesn't count
if(intervalId !== true){
window.activeIntervals--;
}
return window.originalClearInterval(intervalId);
};
We've just published a package solving this exact issue.
npm install time-events-manager
With that, you can view and manage them via timeoutCollection object (and javascript's intervals viaintervalCollection object).
timeoutCollection.getScheduled();
timeoutCollection.getCompleted();
timeoutCollection.getAll();
I just needed something like this and this is what I've put together:
window.setInterval = function (window, setInterval) {
if (!window.timers) {
window.timers = {};
}
if (!window.timers.intervals) {
window.timers.intervals = {};
}
if (!window.timers.intervals.active) {
window.timers.intervals.active = {};
}
return function (func, interval) {
var id = setInterval(func, interval);
window.timers.intervals.active[id] = func;
return id;
}
}(window, window.setInterval);
window.clearInterval = function (window, clearInterval) {
if (!window.timers) {
window.timers = {};
}
if (!window.timers.intervals) {
window.timers.intervals = {};
}
if (!window.timers.intervals.inactive) {
window.timers.intervals.inactive = {};
}
return function (id) {
if (window.timers.intervals.active && window.timers.intervals.active[id]) {
window.timers.intervals.inactive[id] = window.timers.intervals.active[id];
clearInterval(id);
delete window.timers.intervals.active[id];
}
}
}(window, window.clearInterval);
This records the interval ids along with their functions, and also keeps track of their status (active/inactive).
Based on #Alessio's answer. Below is my version. Has a bit more functionality for logging and inspection.
Here is some boilerplate that you can alter to utilize your own frameworks:
var s$ = function (s){return new String(s)}
var _w=window
_w.q$ = {
getMachineTimeMS: function(){
var d = new Date(), ms = d.getMilliseconds()
var a = [d.getHours(), d.getMinutes(), d.getSeconds(), '-', ms<10?'00' + s$(ms):ms<100?'0'+s$(ms):ms]
return a.join('')
}
,getCaller: function(opts){
return "(implement this)"
}
}
Here is the main code:
_w.setTimeout = function (orig_setTimeout) {
var t=(_w._Timers = _w._Timers||{})
var d=(t.Timeouts = t.Timeouts||{})
d.Active = d.Active||{}
t.z_to_id_idx = t.z_to_id_idx||{}
return function (h, n) {
var t = _w._Timers, d = t.Timeouts
var id = orig_setTimeout(h, n), ts = q$.getMachineTimeMS()
var c = q$.getCaller({depth:2})
t.z_to_id_idx[s$(id)] = d.Active[ts] = {sts: ts, id: id, h: h, n: n, scaller: c}
return id;
}
}(_w.setTimeout);
_w.clearTimeout = function (orig_clearTimeout) {
var t=_w._Timers, d = t.Timeouts
d.Inactive = d.Inactive||{}
return function new_clearTimeout(id) {
var t = _w._Timers, d = t.Timeouts, sId = s$(id)
if (!d.Active || !sId in t.z_to_id_idx) return
var r = t.z_to_id_idx[sId]
r.ccaller = q$.getCaller({depth:2})
r.cts = q$.getMachineTimeMS()
d.Inactive[r.ts] = r;
orig_clearTimeout(r.id);
delete d.Active[r.ts]
delete t.z_to_id_idx[sId]
}
}(_w.clearTimeout);
_w.setInterval = function (orig_setInterval) {
var t=(_w._Timers = _w._Timers||{})
var d=(t.Intervals = t.Intervals||{})
d.Active = d.Active||{}
t.z_in_id_idx = t.z_in_id_idx||{}
return function (h, n) {
var t = _w._Timers, d = t.Intervals
var id = orig_setInterval(h, n), ts = q$.getMachineTimeMS()
var c = q$.getCaller({depth:2})
t.z_in_id_idx[s$(id)] = d.Active[ts] = {sts: ts, id: id, h: h, n: n, scaller: c}
return id;
}
}(_w.setInterval);
_w.clearInterval = function (orig_clearInterval) {
var t=_w._Timers, d = t.Intervals
d.Inactive = d.Inactive||{}
return function new_clearInterval(id) {
var t = _w._Timers, d = t.Intervals, sId = s$(id)
if (!d.Active || !sId in t.z_in_id_idx) return
var r = t.z_in_id_idx[sId]
r.ccaller = q$.getCaller({depth:2})
r.cts = q$.getMachineTimeMS()
d.Inactive[r.ts] = r;
orig_clearInterval(r.id);
delete d.Active[r.ts]
delete t.z_in_id_idx[sId]
}
}(_w.clearInterval);
Usage example:
id = setTimeout(()=>{console.log("CALLED")}, 10000)
clearTimeout(id)
setInterval(()=>{console.log("CALLED")}, 1000)
console.table(_w._Timers.Timeouts.Inactive)
The console.table will output a nicely formatted and inspectable table in the JavaScript Console
I'm writing an application that utilizes JavaScript timeouts and intervals to update the page. Is there a way to see how many intervals are setup? I want to make sure that I'm not accidentally going to kill the browser by having hundreds of intervals setup.
Is this even an issue?
I don't think there is a way to enumerate active timers, but you could override window.setTimeout and window.clearTimeout and replace them with your own implementations which do some tracking and then call the originals.
window.originalSetTimeout = window.setTimeout;
window.originalClearTimeout = window.clearTimeout;
window.activeTimers = 0;
window.setTimeout = function(func, delay) {
window.activeTimers++;
return window.originalSetTimeout(func, delay);
};
window.clearTimeout = function(timerID) {
window.activeTimers--;
window.originalClearTimeout(timerID);
};
Of course, you might not always call clearTimeout, but this would at least give you some way to track what is happening at runtime.
I made a Chrome DevTools extension that shows all intervals. Cleared ones are greyed out.
setInterval-sniffer
Instead of just have a count of timers, here is an implementation which stores all timerid's into an array. It only shows active timers while the accepted answer only counts calls to setTimeout & clearTimeout.
(function(w) {
var oldST = w.setTimeout;
var oldSI = w.setInterval;
var oldCI = w.clearInterval;
var timers = [];
w.timers = timers;
w.setTimeout = function(fn, delay) {
var id = oldST(function() {
fn && fn();
removeTimer(id);
}, delay);
timers.push(id);
return id;
};
w.setInterval = function(fn, delay) {
var id = oldSI(fn, delay);
timers.push(id);
return id;
};
w.clearInterval = function(id) {
oldCI(id);
removeTimer(id);
};
w.clearTimeout = w.clearInterval;
function removeTimer(id) {
var index = timers.indexOf(id);
if (index >= 0)
timers.splice(index, 1);
}
}(window));
This is how you can get the count of active timers on the page:
timers.length;
This is how you can remove all active timers:
for(var i = timers.length; i--;)
clearInterval(timers[i]);
Known limitations:
You can only pass a function (not a string) to setTimeout with this monkey patch.
The function assumes clearInterval and clearTimeout do the same, which they do but it could change in the future.
Seeing as Paul has only covered setTimeout I thought I would share a counter for setInterval/clearInterval.
window.originalSetInterval = window.setInterval;
window.originalClearInterval = window.clearInterval;
window.activeIntervals = 0;
window.setInterval = function (func, delay)
{
if(func && delay){
window.activeIntervals++;
}
return window.originalSetInterval(func,delay);
};
window.clearInterval = function (intervalId)
{
// JQuery sometimes hands in true which doesn't count
if(intervalId !== true){
window.activeIntervals--;
}
return window.originalClearInterval(intervalId);
};
We've just published a package solving this exact issue.
npm install time-events-manager
With that, you can view and manage them via timeoutCollection object (and javascript's intervals viaintervalCollection object).
timeoutCollection.getScheduled();
timeoutCollection.getCompleted();
timeoutCollection.getAll();
I just needed something like this and this is what I've put together:
window.setInterval = function (window, setInterval) {
if (!window.timers) {
window.timers = {};
}
if (!window.timers.intervals) {
window.timers.intervals = {};
}
if (!window.timers.intervals.active) {
window.timers.intervals.active = {};
}
return function (func, interval) {
var id = setInterval(func, interval);
window.timers.intervals.active[id] = func;
return id;
}
}(window, window.setInterval);
window.clearInterval = function (window, clearInterval) {
if (!window.timers) {
window.timers = {};
}
if (!window.timers.intervals) {
window.timers.intervals = {};
}
if (!window.timers.intervals.inactive) {
window.timers.intervals.inactive = {};
}
return function (id) {
if (window.timers.intervals.active && window.timers.intervals.active[id]) {
window.timers.intervals.inactive[id] = window.timers.intervals.active[id];
clearInterval(id);
delete window.timers.intervals.active[id];
}
}
}(window, window.clearInterval);
This records the interval ids along with their functions, and also keeps track of their status (active/inactive).
Based on #Alessio's answer. Below is my version. Has a bit more functionality for logging and inspection.
Here is some boilerplate that you can alter to utilize your own frameworks:
var s$ = function (s){return new String(s)}
var _w=window
_w.q$ = {
getMachineTimeMS: function(){
var d = new Date(), ms = d.getMilliseconds()
var a = [d.getHours(), d.getMinutes(), d.getSeconds(), '-', ms<10?'00' + s$(ms):ms<100?'0'+s$(ms):ms]
return a.join('')
}
,getCaller: function(opts){
return "(implement this)"
}
}
Here is the main code:
_w.setTimeout = function (orig_setTimeout) {
var t=(_w._Timers = _w._Timers||{})
var d=(t.Timeouts = t.Timeouts||{})
d.Active = d.Active||{}
t.z_to_id_idx = t.z_to_id_idx||{}
return function (h, n) {
var t = _w._Timers, d = t.Timeouts
var id = orig_setTimeout(h, n), ts = q$.getMachineTimeMS()
var c = q$.getCaller({depth:2})
t.z_to_id_idx[s$(id)] = d.Active[ts] = {sts: ts, id: id, h: h, n: n, scaller: c}
return id;
}
}(_w.setTimeout);
_w.clearTimeout = function (orig_clearTimeout) {
var t=_w._Timers, d = t.Timeouts
d.Inactive = d.Inactive||{}
return function new_clearTimeout(id) {
var t = _w._Timers, d = t.Timeouts, sId = s$(id)
if (!d.Active || !sId in t.z_to_id_idx) return
var r = t.z_to_id_idx[sId]
r.ccaller = q$.getCaller({depth:2})
r.cts = q$.getMachineTimeMS()
d.Inactive[r.ts] = r;
orig_clearTimeout(r.id);
delete d.Active[r.ts]
delete t.z_to_id_idx[sId]
}
}(_w.clearTimeout);
_w.setInterval = function (orig_setInterval) {
var t=(_w._Timers = _w._Timers||{})
var d=(t.Intervals = t.Intervals||{})
d.Active = d.Active||{}
t.z_in_id_idx = t.z_in_id_idx||{}
return function (h, n) {
var t = _w._Timers, d = t.Intervals
var id = orig_setInterval(h, n), ts = q$.getMachineTimeMS()
var c = q$.getCaller({depth:2})
t.z_in_id_idx[s$(id)] = d.Active[ts] = {sts: ts, id: id, h: h, n: n, scaller: c}
return id;
}
}(_w.setInterval);
_w.clearInterval = function (orig_clearInterval) {
var t=_w._Timers, d = t.Intervals
d.Inactive = d.Inactive||{}
return function new_clearInterval(id) {
var t = _w._Timers, d = t.Intervals, sId = s$(id)
if (!d.Active || !sId in t.z_in_id_idx) return
var r = t.z_in_id_idx[sId]
r.ccaller = q$.getCaller({depth:2})
r.cts = q$.getMachineTimeMS()
d.Inactive[r.ts] = r;
orig_clearInterval(r.id);
delete d.Active[r.ts]
delete t.z_in_id_idx[sId]
}
}(_w.clearInterval);
Usage example:
id = setTimeout(()=>{console.log("CALLED")}, 10000)
clearTimeout(id)
setInterval(()=>{console.log("CALLED")}, 1000)
console.table(_w._Timers.Timeouts.Inactive)
The console.table will output a nicely formatted and inspectable table in the JavaScript Console