How to stop all timeouts and intervals using javascript? [duplicate] - javascript

This question already has answers here:
javascript: Clear all timeouts?
(13 answers)
Closed 6 years ago.
I'm working on an ajax web appliation which contains many running timeouts and intervals. And now I need to clear all running timeouts and intervals sometimes. Is there a simple way to stop everything without need to store every timeout and interval ID and iterate through them and clear them?

Sometimes it's possible to save the timer Id / Handle to clear it later which would be the best solution. So this is a second best. But I wanted to give a better understanding of what's going on. It basically grabs the highest timer id and clears everything less than that. But it's also possible to clear other timers that you do not want to clear!
It is a little hackish, so be warned!
// Set a fake timeout to get the highest timeout id
var highestTimeoutId = setTimeout(";");
for (var i = 0 ; i < highestTimeoutId ; i++) {
clearTimeout(i);
}

Updated answer after reading the duplicate I closed this question with -
It works and tested in Chrome on OSX
// run something
var id1 = setInterval(function() { console.log("interval", new Date())}, 1000);
var id2 = setTimeout(function() { console.log("timeout 1", new Date())}, 2000);
var id3 = setTimeout(function() { console.log("timeout 2", new Date())}, 5000); // not run
setTimeout(function() { console.log("timeout 3", new Date())}, 6000); // not run
// this will kill all intervals and timeouts too in 3 seconds.
// Change 3000 to anything larger than 10
var killId = setTimeout(function() {
for (var i = killId; i > 0; i--) clearInterval(i)
}, 3000);
console.log(id1, id2, id3, killId); // the IDs set by the function I used
NOTE: Looked at window objects that had a typeof number - funnily enough IE assigns an 8 digit number, FF a single digit starting with 2

Here is a workaround.
window.timeoutList = new Array();
window.intervalList = new Array();
window.oldSetTimeout = window.setTimeout;
window.oldSetInterval = window.setInterval;
window.oldClearTimeout = window.clearTimeout;
window.oldClearInterval = window.clearInterval;
window.setTimeout = function(code, delay) {
var retval = window.oldSetTimeout(code, delay);
window.timeoutList.push(retval);
return retval;
};
window.clearTimeout = function(id) {
var ind = window.timeoutList.indexOf(id);
if(ind >= 0) {
window.timeoutList.splice(ind, 1);
}
var retval = window.oldClearTimeout(id);
return retval;
};
window.setInterval = function(code, delay) {
var retval = window.oldSetInterval(code, delay);
window.intervalList.push(retval);
return retval;
};
window.clearInterval = function(id) {
var ind = window.intervalList.indexOf(id);
if(ind >= 0) {
window.intervalList.splice(ind, 1);
}
var retval = window.oldClearInterval(id);
return retval;
};
window.clearAllTimeouts = function() {
for(var i in window.timeoutList) {
window.oldClearTimeout(window.timeoutList[i]);
}
window.timeoutList = new Array();
};
window.clearAllIntervals = function() {
for(var i in window.intervalList) {
window.oldClearInterval(window.intervalList[i]);
}
window.intervalList = new Array();
};
It works for set/clear timeout/interval functions called after these lines are executed. Try and see it works:
setInterval('console.log(\'a\')', 1000);
setInterval('console.log(\'b\')', 500);
setInterval('console.log(\'c\')', 750);
setTimeout('clearAllIntervals()', 10000);
Proxying does the magic.

var noofTimeOuts = setTimeout('');
for (var i = 0 ; i < noofTimeOuts ; i++) clearTimeout(i);

var max = setTimeout(function(){ /* Empty function */ },1);
for (var i = 1; i <= max ; i++) {
window.clearInterval(i);
window.clearTimeout(i);
if(window.mozCancelAnimationFrame)window.mozCancelAnimationFrame(i); // Firefox
}

There's nothing built-in, but it's pretty easy to blast through all currently outstanding deferred execution functions by calling this clearAll() function:
function clearAll() {
for (var i = setTimeout(function() {}, 0); i > 0; i--) {
window.clearInterval(i);
window.clearTimeout(i);
if (window.cancelAnimationFrame) window.cancelAnimationFrame(i);
}
}
If you are in charge of the page you run, and can wrap the native deferred execution functions in wrappers that do the house keeping for of course equip each setter function with a corresponding .clearAll() too:
(function(deferFunctions) {
for (var setter in deferFunctions) (function(setter, clearer) {
var ids = [];
var startFn = window[setter];
var clearFn = window[clearer];
function clear(id) {
var index = ids.indexOf(id);
if (index !== -1) ids.splice(index, 1);
return clearFn.apply(window, arguments);
}
function set() {
var id = startFn.apply(window, arguments);
ids.push(id);
return id;
}
set.clearAll = function() { ids.slice(0).forEach(clear); };
if (startFn && clearFn) {
window[setter] = set;
window[clearer] = clear;
}
})(setter, deferFunctions[setter]);
})(
{ setTimeout: 'clearTimeout'
, setInterval: 'clearInterval'
, requestAnimationFrame: 'cancelAnimationFrame'
});
To try that it works, you could then try doing this, for instance, which will remain silent, as none of the callbacks end up firing before they're cancelled again:
// Some example timers of all types:
requestAnimationFrame(console.error);
setInterval(console.info, 1000, 'interval');
setTimeout(alert, 0, 'timeout');
// Now you can clear all deferred functions
// by execution type, whenever you want to:
window.setTimeout.clearAll();
window.setInterval.clearAll();
window.requestAnimationFrame.clearAll();

A little hack added to Gokhan Ozturk's answer
If you are using third party libraries which uses Timeouts and Intervals then they will also be cleared, so I added one parameter to notify function that this interval is to be push'ed or not to array.
window.setTimeout = function(code, delay, toBeAdded) {
var retval = window.oldSetTimeout(code, delay);
var toBeAdded = toBeAdded || false;
if(toBeAdded) {
window.timeoutList.push(retval);
}
return retval;
};
... // likewise for all functions.

You might be better off creating a scheduler. Take a look at this approach by Nader Zeid:
https://www.onsip.com/blog/avoiding-javascript-settimeout-and-setinterval-problems
It's an approach that help create some determinacy (because "the time interval argument of each of those functions really only establishes that the given function will execute after at least that amount of time. So a timed event can miss its target by literally any amount of time.").
Specifically, to the question you raise here, you can easily add and remove functions from the queue. While this response is long after the question was raised, hopefully it's helpful to any who find themselves struggling with Timeouts and Intervals.

You cannot clear any timeouts and intervals you don't know about.
You'd need something like getTimeoutList which isn't in the DOM3 spec, or even planned, AFAIK.

The previous proxying trick is nice, but if you have a lot of timeouts and intervals, I would not fill the arrays with consecutive numbers [1,2,3....], but with intervals. For example, instead of having [1,2,3,7,8,9], you would have maybe something like ['1-3','7-9'] or [[1,3],[7,9]], as a memory optimization. Of course this trick is only suited if you have a lot of timeouts and intervals and also if you would not stop arbitrary intervals that often.

Related

Can't find the way to proper timeout or interval

I can't find the way to timeout or pause (tried for and counting to big numbers) click event. I use alert('x') to stop before $(arr[i-1]).click(); and then I have to press enter after that. But I want to automically pause after click event for about 300 ms.
javascript:
var arr = $("a.farm_icon_b");
var x = document.getElementById("plunder_list").rows;
var images;
for (var i = 0; i < x.length; i++) {
var z = x[i].id;
if (!!z) {
images = document.getElementById(String(z)).getElementsByTagName('img');
for (var j = 0; j < images.length; j++) {
if (!!images) {
if (images[j].src == "https://dspl.innogamescdn.com/8.116/36249/graphic/command/attack.png") {
if (images[j].tooltipText == "1 atak w drodze") {
alert('x');
$(arr[i - 1]).click();
} else if (images[j].tooltipText == "2 ataki w drodze") {
alert('y');
$(arr[i - 1]).click();
}
}
}
}
}
}
You can use javascript setTimeout function to hold or stop your script to execute for time as per your requirement. check this Link for more details. All the best.
setTimeout(function(){
$(arr[i - 1]).click();
}, 3000); // 3000 is stands for 3 second you can change it according to your requirementcode here
Important Note (thanks to #Thomas for highlighting this) This function doesn't "sleep" for 300ms, it freezes the page. The browser simply is stuck in that loop for 300ms and can't do anything untill this is over.
I think this is the function you are looking for. It will help you put a 300ms delay before the button clicks.
function pausecomp(millis)
{
var date = new Date();
var curDate = null;
do { curDate = new Date(); }
while(curDate-date < millis);
}
Source: http://userscripts-mirror.org/scripts/review/408779
Usage:
pausecomp(300); // Sleep for 300ms
$(arr[i - 1]).click();
Disclaimer:
Ideally, a setTimeout() based solution is the best approach for such problems in Javascript (as suggested by Akansh). However, a linear for-loop based approach will most likely not work for such a solution, and you will need to devise a callback functions based solution that relies on deferred execution.

Way to save console.count() as an integer?

I am trying to divide 1 by the console.count() every time it is used. However, this code does not work.
var counter = console.count();
console.log(1/counter);
Any suggestions on how I could do this? I tried doing parseInt but no luck.
Way to save console.count() as an integer?
No. console.count() does not return anything, it directly prints to the console, just like console.log().
Simple implementation of console.count:
var count = (function() {
var counter = {};
return function(v) {
return (counter[v] = (counter[v] || 0) + 1);
}
}());
console.log('foo', count('foo'));
console.log('foo', count('foo'));
console.log('bar', count('bar'));
Here is a script that intercepts messages sent to the console.
var counter = 0;
function takeOverConsole(){
var console = window.console
if (!console) return
function intercept(method){
var original = console[method]
console[method] = function(){
var message = Array.prototype.slice.apply(arguments).join(' ')
// do sneaky stuff
if (original.call){
// Do this for normal browsers
original.call(console, message)
}else{
// Do this for IE
original(message)
}
counter++;
}
}
var methods = ['log', 'warn', 'error', 'count']
for (var i = 0; i < methods.length; i++)
intercept(methods[i])
}
To take Felix King's answer a bit further. Here is a more accurate way to intercept and count anything being sent to the console.
You can customize the function a bit and add any methods you want to track and count.
All console methods
I talk about it more here

Int from for loop not working in function - JS

I am pretty new to Javascript and have noticed this odd issue come up.
var dispatchMouseEvent = function(target, var_args) {
var e = document.createEvent("MouseEvents");
e.initEvent.apply(e, Array.prototype.slice.call(arguments, 1));
target.dispatchEvent(e);
};
var Level1Cats = document.getElementsByClassName("p-pstctgry-lnk-ctgry "); //GETTING LEVEL 1 CATS
var Level1CatsLen = Level1Cats.length; //GETTING LEVEL 1 CAT LEN
for (i = 0; i <= Level1CatsLen-1; i++) {
var ID1 = Level1Cats[i].id;
var temp1 = Level1Cats[i].innerHTML;
temp1.replace(/&/gi, "&").replace(/<[^>]*>/gi, "");
function GoToLevel2(callback) { //GO TO NEXT LEVEL!
dispatchMouseEvent(Level1Cats[i], "mouseover", true, true);
dispatchMouseEvent(Level1Cats[i], "click", true, true);
}
function GetLevel2() { //GET NEXT LEVEL
var Level2Cats = document.getElementsByClassName("p-pstctgry-lnk-ctgry");
return Level2Cats.length;
}
setTimeout(function() { //RUN IT WITH TIMING
GoToLevel2();
}, 100);
var Level2CatsLen = GetLevel2();
}
When the code is executed it gives me an error (Cannot read property 'dispatchEvent' of undefined)
I know this is because the i in the function does not seem to work. If I simply replace it with an int value of 1 it will execute and click cat 1, 16 times, as expected..
I would have thought this should work, any ideas how I can work around it?
Inside the loop, a closure GoToLevel2 is created, closing over the variable i, when i is 1. Then the loop runs through, i is incremented to 2, and the loop is terminated.
Then your setTimeout fires after 100ms, and invokes your closure. It still remembers that there was once a variable i, but it now contains 2. Level1Cats[2] is undefined, and you get an error.
The standard solution is to enclose the contents of the loop into another self-evaluating function that will not close over i:
for (i = 0; i <= Level1CatsLen-1; i++) {
(function(i) {
// ...
})(i);
}
(Note also that setTimeout(function() { GoToLevel2(); }, 200) is identical to, but less efficient than setTimeout(GoToLevel2, 200).)

Having some issues with my non-blocking Javascript

this.plotted = [jQuery('#img1'), jQuery('#img2'), jQuery('#img3')];
Blah.prototype.animate = function()
{
if (!this.plotted.length)
throw 'Blah::animate - No points have been plotted';
// fix the scope
var _this = this;
var animateOn = function(image)
{
image.attr('src', _this.options.pointActive);
setTimeout(function() { animateOff(point); }, 700);
}
var animateOff = function(image)
{
image.attr('src', _this.options.pointDefault);
setTimeout(function() { animateOn(point); }, 700);
}
for (var i = 0; i < this.plotted.length; i++)
{
var point = this.plotted[i];
setTimeout(function() { animateOn(point); }, 700);
}
}
I'm trying to animate these 3 images by switching their src between an 'on' and 'off' image. I don't want this to be 'sequential'. Ie, I don't want to see the first image change, then the second and then the third.
I'm using setTimeout to accomplish this. Well, I'm trying to...
Firstly, the problem I'm having is with the setTimeout inside the for loop.
for (var i = 0; i < this.plotted.length; i++)
{
var point = this.plotted[i];
console.log(point); // this correctly shows each image point
setTimeout(function()
{
console.log(point); // this shows the same, first, point
animateOn(point);
}, 700);
}
I have no idea what the inner point isn't matching the outer point :/
Also, I would like to know if this method is, well, stupid. Will these nested function calls continually build onto the stack and eventually cause me to run out of RAM? Is there a better way to approach this?
This doesn't work because of how closures work.
I'd do it like this:
var makeAnimateStarter = function(point) {
return function() {
animateOn(point);
};
};
for (var i = 0; i < this.plotted.length; i++)
{
var point = this.plotted[i];
setTimeout(makeAnimateStarter(point), 700);
}
And it's not a problem from a stack point of view. Every time a timeout is executed, it's in a new call stack. That's why you require _this. setTimeout() is not suspending the thread at that point and then resuming it's executing the function fresh.

How to use setTimeout / .delay() to wait for typing between characters

I am creating a simple listbox filter that takes the user input and returns the matching results in a listbox via javascript/jquery (roughly 5000+ items in listbox). Here is the code snippet:
var Listbox1 = $('#Listbox1');
var commands = document.getElementById('DatabaseCommandsHidden'); //using js for speed
$('#CommandsFilter').bind('keyup', function() {
Listbox1.children().remove();
for (var i = 0; i < commands.options.length; i++) {
if (commands.options[i].text.toLowerCase().match($(this).val().toLowerCase())) {
Listbox1.append($('<option></option>').val(i).html(commands.options[i].text));
}
}
});
This works pretty well, but slows down somewhat when the 1st/2nd char's are being typed since there are so many items.
I thought a solution I could use would be to add a delay to the textbox that prevents the 'keyup' event from being called until the user stops typing. The problem is, I'm not sure how to do that, or if its even a good idea or not.
Any suggestions/help is greatly appreciated.
You can do a delay like this:
$('#CommandsFilter').keyup(function() {
clearTimeout($.data(this, 'timer'));
var wait = setTimeout(search, 500);
$(this).data('timer', wait);
});
function search() {
var temp = $("<select />");
for (var i = 0; i < commands.options.length; i++) {
if (commands.options[i].text.toLowerCase().match($(this).val().toLowerCase())) {
$('<option></option>', { val: i, html: commands.options[i].text }).appendTo(temp);
}
}
Listbox1.empty().append(temp.children());
}
This stores a timeout on the element you're typing in, if 500ms (adjust as needed) passes between keystrokes, a search executes. Also this appends the elements in a document fragment then into the DOM (still preserving encoding, etc). Depending on the number of items, this may be a decent performance boost as well.
If the commands drop-down isn't changing, I'd suggest the following (note I've dropped jQuery for better performance and compatibility). There are several improvements:
Timer to delay updating the filtered list once half a second has elapsed since the last keypress
List of command texts is pre-cached
Unnecessary use of match replaced with indexOf
Uses fast native DOM manipulation that works in all scriptable browsers since the 1990s
A quick test suggests that for a drop-down with 5000 options containing short strings, it's between 10 and 30 times faster than the jQuery equivalent in most browsers.
Code:
var commands = document.getElementById("DatabaseCommandsHidden");
var filteredDropDown = document.getElementById("Listbox1");
var filterInput = document.getElementById("CommandsFilter");
var timer;
// Create a cached list of the lower case text of the commands drop-down
var commandTexts = [], commandText;
for (var i = 0, len = commands.options.length; i < len; ++i) {
commandText = commands.options[i].text;
commandTexts.push({original: commandText, lower: commandText.toLowerCase()});
}
function populateFilteredDropDown() {
timer = null;
var val = filterInput.value.toLowerCase(), commandText;
var opts = filteredDropDown.options;
filteredDropDown.length = 0;
for (var i = 0, len = commandTexts.length; i < len; ++i) {
commandText = commandTexts[i];
if (commandText.lower.indexOf(val) > -1) {
opts[opts.length] = new Option(commandText.original);
}
}
}
filterInput.onkeyup = function() {
if (timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(populateFilteredDropDown, 500);
};

Categories

Resources