Loop a function using timeout and pass variables - javascript

Alright I have a feeling this is simple and I'm overlooking something.
I have an array of data that I'm passing into the function containing 300 rows.
The function itself picks out a random box to update, and a random array element to pass (0-299) and then updates that box.
The first Iteration is fine. The second returns "Uncaught ReferenceError: rand_ad is not defined "
function loop(last_ad, last_ad_box, ads_array){
// start
while(rand_ad == last_ad){
var rand_ad = get_rand(299);
}
while(rand_ad_box == last_ad_box){
var rand_ad_box = get_rand(29);
}
console.log(ads_array[rand_ad]);
// update the ad
// update_ad('.addno-'+rand_ad_box, ads_array[rand_ad]);
//recall itself to continue looping after 1 second
t = setTimeout("loop(rand_ad, rand_ad_box, ads_array)",3000);
}
function get_rand(max){
var rand = Math.floor(Math.random()*max) + 1;
return rand;
}
I think it might be the quotation marks around the function loop, that it's treating the variables inside as strings instead of actual variables, but I can't get it to render out before it snaggs the error.
Any ideas?

Your guess is correct. Change the timeout line to this:
t = setTimeout(loop, 3000, rand_ad, rand_ad_box, ads_array);
Passing strings to setTimeout is a security risk and not recommended. Also, it doesn't execute the code until the timeout occurs, so the variables are dereferenced after the function has exited.
If you need it to work in IE, then you'll have to use this:
t = setTimeout(function () {
loop(rand_ad, rand_ad_box, ads_array);
}, 3000);

Related

Variable has different values inside and outside a function

I'm trying to set the value of an ID in the variable newID.
To "fill" the variable I'm reading a model like this:
press: function() {
var newID = 0;
oModel.read("/Delivery", null, null, true, function(oData) {
newID = oData.results[oData.results.length - 1].ID;
console.log(newID);
}
console.log(newID);
}
The first console.log gives me the value that I want, but the second one gives me 0 (the first condition set). I've tried using the incremental operator += and using this.newID but all in vain.
It really is a silly problem, probably something the way I'm calling newID on the console or the way I increment it inside the function...
Thanks in advance.
This was going to be a comment but it is so huge, so take it as a comment:
It is possible that the oModel.read function is asynchronous so when you execute the code basically that happen is this:
1) You declare the variable as 0.
2) You execute read function.
3) You execute console.log(var) => this print 0. (Because read does not finished yet and it is still 0 at this point).
4) Read function finished it work assign the desired value to var and execute console.log(var) => this print the desired value.
If that is the case you can implement something like this:
var press = function() {
var newId = 0;
function callback() {
console.log(newId);
}
oModel.read("/Delivery", null, null, true, function(oData) {
newID = oData.results[oData.results.length - 1].ID;
console.log(newID);
callback();
}
}

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.

Send setTimeout name, element id, and time as parameters of a function?

This is probably something really obvious, but I've searched around and tried a few things, and can't get it to work, so maybe someone can point out my error here.
I have a setTimeout that I will end up using over and over (and I know there is the setinterval, but I actually need to control when the timer starts and stops, and whether it starts again each time). Anyway, I figured if I'm writing it over and over, I should be able to use a function and pass it the parameters needed.
if ($('#selectRole').val() === 'Dispatch') {
//show Add Notes button
var funcAddNotesTimer = function(timerName,buttonName, timeToHide) {
console.log(timerName);
console.log(timeToHide / 1000);
timerName = setTimeout(function() {
$('buttonName').show();
}, timeToHide);
};
funcAddNotesTimer('addNotesTimer', '#disAddNotes', 30000);
I'm trying to set the timer function name to 'addNotesTimer', and when the timer is up I want to show the button with id #disAddNotes, and I want the timer to run for 30000 msec.
To me, what I have looks right, but I never get anything in my console log, so I don't think it's even getting into the function.
What am I doing wrong here?
I dont think its possible to use a string argument as the name of setTimeOut
Heres how you could approach it
// var timer = null; // dont really need that
var funcAddNotesTimer = function(buttonName, timeToHide) {
var timerName = setTimeout(function() {
//$('buttonName').show();
$(buttonName).show(); // buttonName is already a string so no need to add quotes around it.
}, timeToHide);
return timerName;
};
if ($('#selectRole').val() === 'Dispatch') {
var timer = funcAddNotesTimer('#disAddNotes', 30000);
// do something with timer
}
when you don't see any output in the console the reason must be something else(e.g. there is a bracket missing at the end of the code)
To set a variable with a dynamic name use the subscript-notation:
window[timerName] = setTimeout(/**/);
it will set a global variable named addNotesTimer.
As you currently do it you're simply overwriting the argument passed to the function.
Summary:
if ($('#selectRole').val() === 'Dispatch') {
//show Add Notes button
var funcAddNotesTimer = function(timerName,buttonName, timeToHide) {
console.log(timerName);
console.log(timeToHide / 1000);
window[timerName] = setTimeout(function() {
$(buttonName).show();
}, timeToHide);
};
funcAddNotesTimer('addNotesTimer', '#disAddNotes', 5000);
}

Javascript - getting return value for setTimeout in recursive function

I have a recursive function which contains some drawing code inside. I was advised to use setTimeout as my drawing was not being displayed until the end of exection. First I put just the drawing code inside setTimeout but this did not help, however when I put the main recursive loop inside setTimeout the drawing worked perfectly, as shown below.
However I need to use the return value of setTimeout (i.e. state as shown below). How can I get this return value when using setTimeout, or solve this problem in another way.
var doLearning = function(time, observedData, state, domain, sampleAction, selectModel, numSamples, depth, discount, stateQueries) {
if(stateQueries[0](time, state) === true) {
console.log("New Round");
var currentModel = selectModel(observedData, 10, stateQueries);
var bestAction = sparseSampleMcmc(depth, numSamples, discount, currentModel, state, sampleAction, stateQueries);
var newStateReward = domain.executeAction(bestAction, stateQueries);
observedData.push(bestAction, newStateReward[1], newStateReward[0]);
console.log(time);
setTimeout(doLearning, 100, time + 1, observedData, newStateReward[0], domain, sampleAction, selectModel, numSamples, depth, discount, stateQueries);
} else {
console.log("Game Over");
return state;
}
}
Make an object with all your vars, like:
var game = {
time: ... ,
observedData: ....,
state: .... etc
}
In doLearning get and modify this object's properties when necessary:
var doLearning = function(obj) {
if(obj.state == ....)
obj.currentModel = whatever...
obj.bestAction = whatever...
setTimeout(function() { doLearning(obj) }, 100)
else
game over
}
This gives me the willies to say, but what if you had a global variable where the return value would go? Set it at the end of doLearning, Then when you detect that the timeout/drawing is done, check the global.
Without all the code I'm having a hard time understanding, but perhaps it would be better to use setInterval here is a simple example.
function draw(){
var state=//whatever
var num = setInerval(doLearning,100)
function doLearning(){
//You have access and can modify state and do not need to return its value
if(){}
else{
clearInterval(num);
console.log('Game over');
}
}
}
By calling as follows, the current execution context (ie. the environment of the current instance of doLearning) forms a closure in which time, observedData etc. remain available to the anonymous function defined inside the setTimeout() statement.
Thus, the following should work:
setTimeout(function(){
doLearning(time + 1, observedData, newStateReward[0], domain, sampleAction, selectModel, numSamples, depth, discount, stateQueries);
}, 100);

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