I want to create a simple game of sorts. I am trying to duplicate a div recursively after a few seconds. After duplicated, it creates the new div with a new unique ID (ID+i).
The idea is that it keeps creating divs and the user has to click on them to remove them for as long as they can before it reaches the max (game over).
It won't properly wait to create the divs. I want to create new divs from the existing one every few seconds, but it either creates all 15 as soon as I run it or it only creates 1 and stops there.
JSFIDDLE -
https://jsfiddle.net/namelesshonor/msrkxq63/
function spawnFly() {
if(x >= 15){
alert("YOU LOST\n15 Flys have infested your screen!");
}
else if(x < 15) {
x++; // adds another fly to the counter
setTimeout(duplicate(), 2000); // spawns a new fly after a few secs
animateDiv(); // animate the spawned fly
spawnFly(); // called recursively until fly count is met
}
};
function duplicate() {
var original = document.getElementById('fly'+i);
var clone = original.cloneNode(true);
clone.id = "fly" + i++;
clone.onclick = swat;
original.parentNode.appendChild(clone);
};
function animateDiv(){
var newq = makeNewPosition();
var oldq = $('.shoo').offset();
var speed = calcSpeed([oldq.top, oldq.left], newq);
$('.shoo').animate({ top: newq[0], left: newq[1] }, speed, function(){
animateDiv();
});
};
The argument to setTimeout should be the function pointer to duplicate, not the result of calling the duplicate function.
setTimeout(duplicate(), 2000);
should be
setTimeout(duplicate, 2000);
Also, you might be intending to call the spawnFly function in the timeout, not the duplicate function. The duplicate function would then be called immediately to "spawn" a new fly. Then in 2 seconds, the spawnFly function is called to duplicate another fly and queue spawnFly again. The way you currently have it set up, the it immediately recurs into the spawnFly function, queuing up 15 flies to spawn in 2 seconds and immediately topping out the fly count (x)
Also, you're your increment of i causes an off by 1 error such that you're always trying to assign the value of the next fly to original. You should use pre-increment (++i) instead of post-increment (i++) to get your desired result
All changes applied:
https://jsfiddle.net/msrkxq63/3/
When you call setTimeout in your example, you're passing the result of duplicate(), not the function duplicate itself as the callback. As duplicate does not return anything, setTimeout tries to call the function undefined. You could either call it this way (as an anonymous callback):
setTimeout(function() { duplicate }, 2000)
or simply,
setTimeout(duplicate, 2000)
If you notice duplicate() in setTimeout(duplicate(),2000);,
it's a function call.
setTimeout 's first parameter is a function.
If you pass duplicate(),
it gets evaluated before the wait and looks for the return value and calls that.
Function or not, it waits after the function call and ends up doing nothing afterthe wait.
So we can say the flow is:
1. Callback = duplicate()(duplicate is called before wait) = <return value of duplicate> instead of the function duplicate itself.
2. Milliseconds = 2000.
3. Call return value after 2 seconds.
The correct code is:
setTimeout(duplicate,2000)//Note that there are no brackets here
Related
In the following code, I tried to keep timeout but it doesn't work. I am sending array and expecting array index with 3 sec delay.
function displayIndex(arr){ // array as input
for(var i=0;i<arr.length; i++){
SetTimeout(function(){
console.log(i); // always returns 4
},3000);
}
}
displayIndex([10,20,30,40])
update:
var arr = [10,20,30,40];
function displayIndex(arr){ // array as input
for(var i=0;i<arr.length; i++){
setTimeout(function () {
var currentI = i; //Store the current value of `i` in this closure
console.log(currentI);
}, 3000);
}
}
displayIndex(arr); // still prints all 4.
Also, tried
arr.forEach(function(curVal, index){
setTimeout(function(){
console.log(index);
},3000);
}); // prints 0 1 2 3 but I do not see 3 secs gap between each display, rather one 3 sec delay before everything got displayed.
Use this:
function displayIndex(arr){ // array as input
var i=0;
var current;
run=setInterval(function(){ // set function inside a variable to stop it later
if (i<arr.length) {
current=arr[i]; // Asign i as vector of arr and put in a variable 'current'
console.log(current);
i=i+1; // i increasing
} else {
clearInterval(run); // This function stops the setInterval when i>=arr.lentgh
}
},3000);
}
displayIndex([10,20,30,40]);
1st: If you use setTimeout or setInterval function inside a for that's a problem 'couse all this are loops ways (the first two are loops with time intervals). Aaand setTimeout just run code inside once.
Note: setInterval need a function to stop it clearInterval, so that's why i put an if inside.
2nd: You are not setting currentI or i like a vector of arr operator. When you run an array the format is: arr[currentI], for example.
Doubts?
SetTimeout should be setTimeout. It's case-sensitive.
You're setting 4 timeouts all at once. Since you're incrementing the value of i every loop, it's going to be 4 at the end of the loop.
I'm not really sure what you're trying to do, but perhaps you wanted this?
setTimeout(function () {
var currentI = i; //Store the current value of `i` in this closure
console.log(currentI);
}, 3000);
The reason why it's behaving unexpectedly:
Case 1: In the first snippet, setTimeout() is adding the functions to the Event Queue to be executed after main thread has no more code left to execute. The i variable was passed as reference and, so the last modified value gets printed on each call since, it was passed by reference.
Case 2: In this case, since you are passing 4 explicit references, the values are different but, the execution order will same ( I.e., synchronous and instantaneous).
Reason: setTimeout() function always pushes the function passed to the queue to be executed with the delay acting as a minimum guarantee that it will run with the delayed interval. However, if there is code in the queue before the function or, any other code in the main thread, the delay will be longer.
Workaround: If you do not to implement blocking behaviour in code, I would suggest using an analogue of process.hrtime() for browser ( there should be a timing method on the window object and, write a while loop that explicitly loops until a second has elapsed.
Suggestion: I am somewhat confused as to why you need such blocking in code?
Running the code, the browser will display RangeError.
function hide() {
h -= step;
pp.style.height = h + "px";
setTimeout(hide(), 1);
}
The problem is this line:
setTimeout(hide(),1);
Rather than telling JavaScript to call hide() again in 1 millisecond you actually call it immediately and only pass it's return value to setTimeout(). This causes an infinite recursion, which in the end causes the stack overflow/error you're getting.
To fix this, you'll have to use either syntax from your question's title:
Pass the function name only rather than calling it (better here).
Pass a lambda function.
Or pass the call inside a string that will be evaluated (IMO should be avoided or replaced with a lambda expression).
However, in your specific scenario I'd suggest using set Timeout(), considering your code is reasonably simple to always finish in time:
// Start the while thing
var handle = setInterval(hide, 1);
// Actual function
function hide()
{
// Do stuff
// End condition
if (done)
clearInterval(handle);
}
This code doesn't terminate, so it creates infinite number of stacks which causes stack overflow.
Consider adding some termination logic, like:
if (h < 0) { return }
This terminates the execution of hide() function when h is less then 0.
I'm assuming h and step are some global vars.
Also you're calling hide immediately and passing the value to setTimeout.
Correct way to recursively call function with timeout will be to pass function as value to setTimeout function:
setTimeout(hide, 1)
which is equivalent to:
setTimeout(function() { hide() }, 1)
My objective is to populate an array dynamically using setInterval function. So I created a global variable array that I can access outside the scope of setInterval function. But when I try to do console.log, its empty.
However, if I try to do console.log inside the setInterval function, I can see the array populating every 1000 ms.
Its supposed to be a global variable so i should be able to see the variable right? How come it cannot be seen outside the function?
below is the code:
var array = [];
var n;
setInterval(function(){
n = Math.random();
if(n < 0.5){
array.push('white');
}else{
array.push('black');
}
// console.log(array); // i can see the array here
}, 1000);
console.log(array); // but i cannot see the array here
UPDATE:
Ok I now know the reason why its empty. So when I put a setTimeout of 5secs, I can now see the contents.
setTimeout(function(){
console.log(array); // i can now see the array!
}, 5000);
I'm pretty sure the array you "can't see" is actually displaying the correct array, an empty one. It gets called before the functions first array.push, so it shows up as empty.
Because you are using setInterval, with a 1 second delay, to add to the array the last line console.log(array); is executed before any of the interval function so the array would still be empty then.
Good info here: http://javascript.info/tutorial/settimeout-setinterval
setInterval schedules a function to be executed asynchronously. This means that the following code is executed first, finished, and only then, some time later, the scheduled function is started.
the reason why I cannot see the array is because theres not enough time for the console to log the contents so by putting another setInterval or setTimeout to delay the execution of console.log, I can now see the contents.
var array = [];
var n;
setInterval(function(){
n = Math.random();
if(n < 0.5){
array.push('meron');
}else{
array.push('wala');
}
// console.log(array); // i can see the array here
}, 1000);
setInterval(function(){
console.log(array); // i can now see the array!
}, 2000);
I am trying to create a FOR loop that removes an element every 1000ms instead of rushing instantaneously through the array and perform the operations.
I am doing this for reasons of performance since going normally through the loop freezes my UI.
function removeFunction (i,selected) {
selected[i].remove();
}
function startLoop() {
var selected = paper.project.selectedItems;
for (var i = 0; i < selected.length; i++) {
if (selected[i].name === "boundingBoxRect") continue;
setTimeout(removeFunction(i,selected),1000)
}
}
It seems that the selected[i].remove() method is getting called without any delay. Why is that? Since I have set a Timeout of 1000ms shouldn't the items get removed with 1000ms interval between each?
Note
In the code above, I am skipping an item called boundingBoxRect since I don't want to remove that. Just stating this so there is no confusion
Simply turn it into a recursive function:
function removeFunction (i, selected) {
// If i is equal to the array length, prevent further execution
if (i === selected.length)
return;
// Remove ith child of selected array
selected[i].remove();
// Trigger same function after 1 second, incrementing i
setTimeout(function() {
removeFunction(++i,selected)
}, 1000);
}
// Trigger the function to begin with, passing in 0 as i
removeFunction(0, paper.project.selectedItems);
Here's a JSFiddle demo (using console.log instead of selected[i].remove(), as you haven't provided a definition for that function);
It seems that the selected[i].remove() method is getting called without any delay. Why is that?
Because that's what you're telling it to do. setTimeout(removeFunction(i,selected),1000) calls removeFunction immediately and passes its return value into setTimeout, exactly the way foo(bar()) calls bar and passes its return value into foo.
You can get the effect you want by using a builder function:
setTimeout(buildRemover(i,selected),1000);
...where buildRemover is:
function buildRemover(index, array) {
return function() {
removeFunction(index, array);
};
}
Note how buildRemover creates a function that closes over the index and array variables. Then it returns a reference to that function, which is what gets scheduled via setTimeout. When the timeout occurs, that generated function is called, and it calls removeFunction with the appropriate values.
You can also do something similar using ES5's Function#bind:
setTimeout(removeFunction.bind(null, i, selected),1000);
Function#bind returns a new function that, when called, will call the original (removeFunction above) use the given this value (null in our example) and arguments.
I have two asynchronous functions the one nested in the other like this:
//Async 1
document.addEventListener("click", function(event){
for (var i in event){
//Async 2
setTimeout(function(){
console.log(i);
}, 200*i);
}
});
What I want is to be able and print each entry(i) of the event object. The output on Firefox is however this:
MOZ_SOURCE_KEYBOARD
MOZ_SOURCE_KEYBOARD
MOZ_SOURCE_KEYBOARD
MOZ_SOURCE_KEYBOARD
..
If I move console.log(i) outside Async 2 then I get the correct result:
type
target
currentTarget
eventPhase
bubbles
cancelable
..
Why doesn't it work correctly when reading the i inside async 2? Shouldn't event be "alive" inside the whole Async 2 block of code as well?
setTimeout uses i as it appears in async1. That is, it references i instead of using the value of i when the timeout function is created. When your timeout function finally runs, it looks at the current value of i, which is the last key in event after the for-loop.
You can avoid this by using a self-calling function, such that the arguments of that function are local to the timeout function:
for (var i in event) {
setTimeout((function(i) {
return function() { console.log(i); }
})(i), 200 * i);
}
Use this:
document.addEventListener("click", function(event){
var count = 1;
for (var i in event){
//Async 2
setTimeout((function(i){
return function () {
console.log(i);
};
})(i), 200 * count++);
}
});
DEMO: http://jsfiddle.net/AQykp/
I'm not exactly sure what you were going for with 200*i, since i is a string (not even digits). But I tried to fix it with counter in my answer, assuming what you really wanted.
So the reason you were seeing the results you were was because of the common closure problem. The setTimeout callback will execute after the for loop completes, leaving i as the last key in event. In order to overcome that, you have to add a closure that captures the value of i at that point in the loop.
Event is indeed alive inside your callback.
The problem is that, by the time your function is executed, the value of i has changed (from what I can see from the output, the loop has ended and i has reached its maximum value) and thus outputting the same value for every callback.
If you use the function Ian commented, you will curry the actual value into a new function. That way the value of i can vary, you captured the current value inside the inner function