Javascript: Can't stop the setTimeout - javascript

I'm working on a proxy server checker and have the following code to start the requests at intervals of roughly 5 seconds using the setTimeout function;
function check() {
var url = document.getElementById('url').value;
var proxys = document.getElementById('proxys').value.replace(/\n/g,',');
var proxys = proxys.split(",");
for (proxy in proxys) {
var proxytimeout = proxy*5000;
t = setTimeout(doRequest, proxytimeout, url, proxys[proxy]);
}
}
However I can't stop them once their started!
function stopcheck() {
clearTimeout(t);
}
A fix or better method will be more that appreciated.
Thank you Stack Overflow Community!

There are 2 major problems with your code:
t is overwritten for each timeout, losing the reference to the previous timeout each iteration.
t is may not be a global variable, thus stopcheck() might not be able to "see" t.
Updated functions:
function check() {
var url = document.getElementById('url').value;
var proxys = document.getElementById('proxys').value.replace(/\n/g,',');
var timeouts = [];
var index;
var proxytimeout;
proxys = proxys.split(",");
for (index = 0; index < proxys.length; ++index) {
proxytimeout = index * 5000;
timeouts[timeouts.length] = setTimeout(
doRequest, proxytimeout, url, proxys[index];
);
}
return timeouts;
}
function stopcheck(timeouts) {
for (var i = 0; i < timeouts.length; i++) {
clearTimeout(timeouts[i]);
}
}
Example of use:
var timeouts = check();
// do some other stuff...
stopcheck(timeouts);

Where is 't' being defined?
It keeps being redefined in the for loop, so you will loose track of each timeout handle...
You could keep an array of handles:
var aTimeoutHandles = new Array();
var iCount = 0;
for (proxy in proxys) {
var proxytimeout = proxy*5000;
aTimeoutHandles[iCount++] = setTimeout(doRequest, proxytimeout, url, proxys[proxy]);
}

Define t outside of both functions first. Additionally, you're overwriting t with each iteration your for loop. Perhaps building a collection of references, and then to stop them you cycle through and clearTimeout on each.

You overwrite t each time you set the interval. Thus you only end up clearing the last one set.

Looks like you're setting multiple timeouts (one for each proxy), but trying to save them in the same variable. You probably need to use an array there, instead of a simple variable.

You have a few problems there:
The main one is that you're overwriting t on each iteration of your for loop; you need an array of ts for your structure to work.
You're using for..in to loop through the indexes of the array. That's not what for..in is for (although there are a lot of people confused about that; see this article). for..in loops through the property names of an object, not the indexes of an array, and therefore this usage breaks in non-trivial situations. Just use an old-fashioned for loop.
You're declaring proxys twice. This is actually harmless, but...
You're not declaring proxy at all (which isn't harmless; it becomes an implicit global).
I've updated the code in Jordan's excellent answer to address those.

Related

Understanding JavaScript setTimeout and setInterval

I need a bit of help understanding and learning how to control these functions to do what I intend for them to do
So basically I'm coming from a Java background and diving into JavaScript with a "Pong game" project. I have managed to get the game running with setInteval calling my main game loop every 20ms, so that's all ok. However I'm trying to implement a "countdown-to-begin-round" type of feature that basically makes a hidden div visible between rounds, sets it's innerHTML = "3" // then "2" then "1" then "GO!".
I initially attempted to do this by putting setTimeout in a 4-iteration for-loop (3,2,1,go) but always only displayed the last iteration. I tried tinkering for a bit but I keep coming back to the feeling that I'm missing a fundamental concept about how the control flows.
I'll post the relevant code from my program, and my question would be basically how is it that I'm writing my code wrong, and what do I need to know about setTimeout and setInterval to be able to fix it up to execute the way I intend it to. I'm interested in learning how to understand and master these calls, so although code examples would be awesome to help me understand and are obviously not unwelcome, but I just want to make it clear that I'm NOT looking for you to just "fix my code". Also, please no jQuery.
The whole program would be a big wall of code, so I'll try to keep it trimmed and relevant:
//this function is called from the html via onclick="initGame();"
function initGame(){
usrScore = 0;
compScore = 0;
isInPlay = true;
//in code not shown here, these objects all have tracking variables
//(xPos, yPos, upperBound, etc) to update the CSS
board = new Board("board");
ball = new Ball("ball");
lPaddle = new LPaddle("lPaddle");
rPaddle = new RPaddle("rPaddle");
renderRate = setInterval(function(){play();}, 20);
}
.
function initNewRound(){
/*
* a bunch of code to reset the pieces and their tracking variables(xPos, etc)
*/
//make my hidden div pop into visibility to display countdown (in center of board)
count = document.getElementById("countdown");
count.style.visibility = "visible";
//*****!!!! Here's my issue !!!!*****//
//somehow i ends up as -1 and that's what is displayed on screen
//nothing else gets displayed except -1
for(var i = 3; i >= 0; i--){
setInterval(function(){transition(i);}, 1000);
}
}
.
//takes initNewRound() for-loop var i and is intended to display 3, 2, 1, GO!
function transition(i){
count.innerHTML = (i === 0) ? "Go" : i;
}
.
//and lastly my main game loop "play()" just for context
function play(){
if(usrScore < 5 && compScore < 5){
isInPlay = true;
checkCollision();
moveBall();
moveRPaddle();
if(goalScored()){
isInPlay = false;
initNewRound();
}
}
}
Thanks a bunch for your advise, I'm pretty new to JavaScript so I really appreciate it.
Expanding on cookie monster's comment, when you use setInterval in a loop, you are queueing up method executions that will run after the base code flow has completed. Rather than queue up multiple setInterval executions, you can queue up a single execution and use a variable closure or global counter to track the current count. In the example below, I used a global variable:
var i = 3 // global counter;
var counterInterval = null; // this will be the id of the interval so we can stop it
function initNewRound() {
// do reset stuff
counterInterval = setInterval(function () { transition() }, 1000); // set interval returns a ID number
}
// we don't need to worry about passing i, because it is global
function transition() {
if (i > 0) {
count.innerHTML = i;
}
else if (i === 0) {
count.innerHTML = "Go!";
}
else {
i = 4; // set it to 4, so we can do i-- as one line
clearInterval(counterInterval); // this stops execution of the interval; we have to specify the id, so you don't kill the main game loop
}
i--;
}
Here is a Fiddle Demo
The problem is in this code:
for(var i = 3; i >= 0; i--){
setInterval(function(){transition(i);}, 1000);
}
When the code runs, it creates a new function 3 times, once for each loop, and then passes that function to setInterval. Each of these new functions refers to the variable i.
When the first new function runs it first looks for a local variable (in it's own scope) called i. When it does not find it, it looks in the enclosing scope, and finds i has the value -1.
In Javascript, variables are lexically scoped; an inner function may access the variables defined in the scope enclosing it. This concept is also known as "closure". This is probably the most confusing aspect of the language to learn, but is incredibly powerful once you understand it.
There is no need to resort to global variables, as you can keep i safely inside the enclosing scope:
function initNewRound(){
var i = 3;
var count = document.getElementById("countdown");
count.style.visibility = "visible";
var interval = setInterval(function(){
//this function can see variables declared by the function that created it
count.innerHTML = i || "Go"; //another good trick
i-=1;
i || clearInterval(interval); //stop the interval when i is 0
},1000);
}
Each call to this function will create a new i, count and interval.

How to avoid access mutable variable from closure

I have some code like this:
for(var id=0; id < message.receiver.length; id++){
var tmp_id = id;
zlib.gzip(JSON.stringify(message.json), function(err, buffer){
...
pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak
delete pushStatusPool[message.receiver[tmp_id]];
...
});
}
And I got a warning that using tmp_id in closure may cause problem because it is a mutable variable.
How could I avoid that? I mean how could I send an immutable variable to callback since this is a for loop and I can not change code of zlib.gzip? Or in other words, how could I pass a argument to a closure?
You need to create a scope to correctly capture tmp_id using a self-executing function. That's because the entire for loop is one scope, meaning each time through, you're capturing the same variable. So the callback will end up with the wrong ids, because temp_id's value will get changed before the callback is called.
I'd ignore (or shut off) the warning, though, which seems to be complaining that because temp_id is mutable, you might reassign it. That's sort of silly. If you really want to fix it, try using the const keyword instead of var.
for(var id=0; id < message.receiver.length; id++){
(function(){
const tmp_id = id;
zlib.gzip(JSON.stringify(message.json), function(err, buffer){
...
pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak
delete pushStatusPool[message.receiver[tmp_id]];
...
});
})();
}
I have faced the same problem and solved it slightly modifying the answer of user24359, by passing the id to the closure:
for(var id=0; id < message.receiver.length; id++){
(function(tmp_id){
zlib.gzip(JSON.stringify(message.json), function(err, buffer){
...
pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak
delete pushStatusPool[message.receiver[tmp_id]];
...
});
})(id);
}
here a simplification of user24359's great answer.
This is the solution:
var object = {a:1,b:2};
for (var y in object){
(function(){const yyy = y;
setTimeout(function(){console.log(yyy)},3000);})();
}
The above code logs a b and is the solution.
The following code logs b b :
var object = {a:1,b:2};
for (var y in object){
setTimeout(function(){console.log(y)},3000);
}
I've faced the same problem in protractor. Solved it using following code -
(function(no_of_agents){
ptor.element.all(by.repeater('agent in agents').column('displayName')).then(function(firstColumn){
console.log(i, '>>>>>Verifying the agent Name');
var agentsSorted = sortAgentsByName();
//verify the agent name
expect(firstColumn[no_of_agents].getText()).toEqual(agentsSorted[no_of_agents].name);
//now click on the agent name link
firstColumn[no_of_agents].click();
ptor.sleep(5000);
});
})(no_of_agents);
#user24359 answer is a good solution but you can simply replace the var keyword by the let keyword.
for(var id=0;
becomes
for(let id=0;
See details here.
Edit : As Heriberto Juárez suggested it, it will only works for browsers that supports EcmaScript6.
Creating closures in a loop with var (tmp_id) being in the upper scope of the callback function is a common mistake that should be avoided due to the var not being block-scoped. Because of this, and because each closure, created in the loop, shares the same lexical environment, the variable will always be the last iterated value (i.e. message.receiver.length - 1 as tmp_id) when the callback function gets invoked. Your IDE detects this behavior and complains rightly.
To avoid the warning, there are several solutions:
Replace var with let ensuring each created closure to have its own scoped tmp_id defined in each iteration:
for (var id = 0; id < message.receiver.length; id++) {
let tmp_id = id;
zlib.gzip(JSON.stringify(message.json), function(err, buffer) {
// Do something with tmp_id ...
});
}
Create a lexical environment in each iteration by leveraging IIFE like gennadi.w did.
Create a callback function in each iteration by using a factory function (createCallback):
const createCallback = tmp_id => function(err, buffer) {
// Do something with tmp_id ...
};
for (var id = 0; id < message.receiver.length; id++) {
zlib.gzip(JSON.stringify(message.json), createCallback(id));
}
bind the variable(s) on the callback function in which they get prepended to its parameters:
for (var id = 0; id < message.receiver.length; id++) {
zlib.gzip(JSON.stringify(message.json), function(tmp_id, err, buffer) {
// Do something with tmp_id (passed as id) ...
}.bind(this, id));
}
If possible, var should be avoided as of ECMAScript 2015 due to such error-prone behaviors.

What's the cleanest way to write a non-blocking for loop in javascript?

So, I've been thinking about a brain teaser - what if I had a large object I for some reason had to iterate through in node js, and didn't want to block the event loop while I was doing that?
Here's an off-the-top-of-my-head example, I'm sure it can be much cleaner:
var forin = function(obj,callback){
var keys = Object.keys(obj),
index = 0,
interval = setInterval(function(){
if(index < keys.length){
callback(keys[index],obj[keys[index]],obj);
} else {
clearInterval(interval);
}
index ++;
},0);
}
While I'm sure there are other reasons for it being messy, this will execute slower than a regular for loop, because setInterval 0 doesn't actually execute every 0 ms, but I'm not sure how to make a loop with the much faster process.nextTick.
In my tests, I found this example takes 7 ms to run, as opposed to a native for loop (with hasOwnProperty() checks, logging the same info), which takes 4 ms.
So, what's the cleanest/fastest way to write this same code using node.js?
The behavior of process.nextTick has changed since the question was asked. The previous answers also did not follow the question as per the cleanliness and efficiency of the function.
// in node 0.9.0, process.nextTick fired before IO events, but setImmediate did
// not yet exist. before 0.9.0, process.nextTick between IO events, and after
// 0.9.0 it fired before IO events. if setImmediate and process.nextTick are
// both missing fall back to the tick shim.
var tick =
(root.process && process.versions && process.versions.node === '0.9.0') ?
tickShim :
(root.setImmediate || (root.process && process.nextTick) || tickShim);
function tickShim(fn) {setTimeout(fn, 1);}
// executes the iter function for the first object key immediately, can be
// tweaked to instead defer immediately
function asyncForEach(object, iter) {
var keys = Object.keys(object), offset = 0;
(function next() {
// invoke the iterator function
iter.call(object, keys[offset], object[keys[offset]], object);
if (++offset < keys.length) {
tick(next);
}
})();
}
Do take note of #alessioalex's comments regarding Kue and proper job queueing.
See also: share-time, a module I wrote to do something similar to the intent of the original question.
There are many things to be said here.
If you have a web application for example, you wouldn't want to do "heavy lifting" in that application's process. Even though your algorithm is efficient, it would still most probably slow down the app.
Depending on what you're trying to achieve, you would probably use one of the following approaches:
a) put your "for in" loop in a child process and get the result in your main app once it's over
b) if you are trying to achieve something like delayed jobs (for ex sending emails) you should try https://github.com/LearnBoost/kue
c) make a Kue-like program of your own using Redis to communicate between the main app and the "heavy lifting" app.
For these approaches you could also use multiple processes (for concurrency).
Now time for a sample code (it may not be perfect, so if you have a better suggestion please correct me):
var forIn, obj;
// the "for in" loop
forIn = function(obj, callback){
var keys = Object.keys(obj);
(function iterate(keys) {
process.nextTick(function () {
callback(keys[0], obj[keys[0]]);
return ((keys = keys.slice(1)).length && iterate(keys));
});
})(keys);
};
// example usage of forIn
// console.log the key-val pair in the callback
function start_processing_the_big_object(my_object) {
forIn(my_object, function (key, val) { console.log("key: %s; val: %s;", key, val); });
}
// Let's simulate a big object here
// and call the function above once the object is created
obj = {};
(function test(obj, i) {
obj[i--] = "blah_blah_" + i;
if (!i) { start_processing_the_big_object(obj); }
return (i && process.nextTick(function() { test(obj, i); }));
})(obj, 30000);
Instead of:
for (var i=0; i<len; i++) {
doSomething(i);
}
do something like this:
var i = 0, limit;
while (i < len) {
limit = (i+100);
if (limit > len)
limit = len;
process.nextTick(function(){
for (; i<limit; i++) {
doSomething(i);
}
});
}
}
This will run 100 iterations of the loop, then return control to the system for a moment, then pick up where it left off, till its done.
Edit: here it is adapted for your particular case (and with the number of iterations it performs at a time passed in as an argument):
var forin = function(obj, callback, numPerChunk){
var keys = Object.keys(obj);
var len = keys.length;
var i = 0, limit;
while (i < len) {
limit = i + numPerChunk;
if (limit > len)
limit = len;
process.nextTick(function(){
for (; i<limit; i++) {
callback(keys[i], obj[keys[i]], obj);
}
});
}
}
The following applies to [browser] JavaScript; it may be entirely irrelevant to node.js.
Two options I know of:
Use multiple timers to process the queue. They will interleave which will give the net effect of "processing items more often" (this is also a good way to steal more CPU ;-), or,
Do more work per cycle, either count or time based.
I am not sure if Web Workers are applicable/available.
Happy coding.

What are the pitfalls of adding properties to a function object within the function?

I found I could use this technique to retain a sort of "state" within an event handler, w/o having to involve outside variables...
I find this technique to be very clever in leveraging the fact that functions are actually objects in and of themselves, but am worried I'm doing something that could have negative implications of some sort...
Example...
var element = document.getElementById('button');
element.onclick = function funcName() {
// attaching properties to the internally named "funcName"
funcName.count = funcName.count || 0;
funcName.count++;
if (self.count === 3) {
// do something every third time
alert("Third time's the charm!");
//reset counter
funcName.count = 0;
}
};
Instead of doing that, you can use a closure:
element.onclick = (function() {
var count = 0;
return function(ev) {
count++;
if (count === 3) {
alert("3");
count = 0;
}
};
})();
That setup involves an anonymous function that the code immediately calls. That function has a local variable, "count", which will be preserved over the succession of event handler calls.
By the way, this:
var something = function dangerous() { ... };
is "dangerous" because some browsers (guess which, though Safari has had issues too) do weird things when you include a name on a function expression like that. Kangax wrote the issue up quite thoroughly.

Recursion function not defined error

Hi i have a problem with recursion.
i followed this example from wc3 http://www.w3schools.com/jsref/met_win_settimeout.asp
But mine seems to not work at all.
function rotateImages(start)
{
var a = new Array("image1.jpg","image2.jpg","image3.jpg", "image4.jpg");
var c = new Array("url1", "url2", "url3", "url4");
var b = document.getElementById('rotating1');
var d = document.getElementById('imageurl');
if(start>=a.length)
start=0;
b.src = a[start];
d.href = c[start];
window.setTimeout("rotateImages(" + (start+1) + ")",3000);
}
rotateImages(0);
Firebug throws the error :
rotateImages is not defined
[Break On This Error] window.setTimeout('rotateImages('+(start+1)+')',3000);
However if i change the timeOut to :
window.setTimeout(rotateImages(start+1),3000);
It recursives but somehow the delay doesn't work and gives me too much recursion(7000 in a sec)
There are many reasons why eval should be avoided, that it breaks scope is one of them. Passing a string to setTimeout causes it to be evaled when the timer runs out.
You should pass a function instead.
window.setTimeout(rotateImages(start+1),3000);
This calls rotateImages immediately, then passes its return value to setTimeout. This doesn't help since rotateImages doesn't return a function.
You probably want:
window.setTimeout(rotateImages,3000,[start+1]);
Or create an anonymous function that wraps a closure around start and pass that instead:
window.setTimeout(function () { rotateImages(start + 1); },3000);
The latter option has better support among browsers.
Be wary of code from W3Schools.
The other answers give a solution. I'll just add that you're recreating the Arrays and repeating the DOM selection every time the rotateImages function is called. This is unnecessary.
You can change your code like this:
(function() {
var a = ["image1.jpg","image2.jpg","image3.jpg", "image4.jpg"];
var c = ["url1", "url2", "url3", "url4"];
var b = document.getElementById('rotating1');
var d = document.getElementById('imageurl');
function rotateImages(start) {
b.src = a[start];
d.href = c[start];
window.setTimeout(function() {
rotateImages( ++start % a.length );
}, 3000);
}
rotateImages(0);
})();
Try this syntax:
window.setTimeout(function() {
rotateImages(start+1);
},3000);
setTimeout() expects a function reference as the 1st parameter. Simply putting a function call there would give the return value of te function as the parameter, this is why the delay did not work. However your first try with evaluating a string was a good approach, but it is not recommended.

Categories

Resources