I'm holding code lines in an array, and trying to run them cell by cell with setTimeout().
This executes the code well:
for (i=0; i<restorePoints.length; i++){
eval(restorePoints[i]);
}
but I want to have a short delay between every iteration: i want to use setTimeout() instead of eval(). for some reason none of those work:
for (i=0; i<restorePoints.length; i++){
setTimeout(restorePoints[i],1000);
}
or
for (i=0; i<restorePoints.length; i++){
setTimeout(eval(restorePoints[i]),1000);
}
how do I do it?
thanks
The loop is fast. It will create all timeouts in a row, so all timeouts will fire at the same time. You can either make the time depended on the loop variable, i.e. increasing the time in every iteration, or, what I would do, use only one timeout and a recursive call:
(function() {
var data = restorePoints;
var run = function(i) {
setTimeout(function() {
var entry = data[i];
if(entry) {
eval(entry);
run(i+1);
}
}, 1000);
};
run(0);
}());
Note that there is a difference between eval(string) and setTimeout(string, ...) apart from the delay:
eval will evaluate the parameter in the current scope while setTimeout (and setInterval) will evaluate it in the global scope.
This might be relevant to you.
If you're going to do it either of those ways, you'll need to wrap the function call in an anonymous function:
for (i=0; i<restorePoints.length; i++){
setTimeout(function(){eval(restorePoints[i]}),1000);
}
Otherwise you're not setting the eval to fire in a timeout, you're setting the result of the executing Javascript code (whatever that might be in this case) to be the thing setTimeout is opperating against.
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?
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 was wondering if the following code could introduce race conditions:
rows.each(function () {
var current = this;
var doOperation = function () {
current.someMethod();
// do some operation using current
};
setTimeout(doOperation, 1);
});
During the settimeout delay, is it possible that the browser will start executing the next itteration of the loop and change "current", so that doOperation doesn't exuecute using the value that was orignally assigned?
There's no danger there, because each iteration's doOperation will close over a new current local variable, not interfering with previous ones.
Everything seems to be in order here. setTimeout is in the closure of the each function, so its doOperation will be different for each row you have.
This loop is in a function and it counts down from 10, however if I alert the parameter passed using i in the setV function it actually counts up!
for (var i=10;i>0;i--){
setTimeout('setV('+i+',"Out")',100);
}
function setV(c,t){
alert(c);
}
All the setV are programmed to execute at the same time (100ms after the instaneous loop execution), the order isn't determined (see the spec).
You probably wanted
for (var i=10;i>0;i--){
setTimeout('setV('+i+',"Out")',100*(11-i));
}
If you are describing the behavior with:
setTimeout('setV('+i+',"Out")',i*100);
the reason it counts up is because a callback set for 1s will execute earlier than one set for 2s, which will execute earlier than one set for 3s...
When all of the timeouts are set to run at the same time, there is no promise what order they will run this.
This is a much better way to write that loop:
function initThis() {
var idx = 0;
function doOneIteration() {
window.alert(idx);
idx++;
if (idx <= 10) {
window.setTimeout(doOneIteration);
}
}
doOneIteration(); // Start loop
}
initThis(); // This makes it all happen
There is continue; to stop the loop and move to the next loop
There is break; to stop the loop and move to the end of the loop
Isn't there some kind of start; that stop the loop and move to the beginning of the loop?
I know it is easy to achieve all of these three actions by just modifying the value of i, but I always try to look for already built-it functions.
Resetting the value of your loop variable to the initial value then calling continue is as close as you'll get.
For example:
for(var i=0; i<20; i++) {
if(somecondition) {
i=-1; continue;
}
}
No - there is no keyword or other way to do it automatically.
As you already mentioned you can just modify the loop condition variable(s) within your loop. Easy if it's a simple i counter, but of course you may have more initialisation to do than just a simple counter.
Or you can do something like the following:
restartLoop:
while (true) {
for (var i=0, j=100000, x="test"; i < 1000; i++, j--, x+= ".") {
if (/*some condition, want to restart the loop*/)
continue restartLoop;
}
break;
}
The continue restartLoop will jump back out to continue with the next iteration of the while loop, which then immediately starts the for loop from the beginning including all of the initialisation code. If the for exits normally the break statement after it will break out of the containing while loop.
I don't really recommend doing this in a general sense, but if your loop initialisation process was really complicated it could be worth it because then you wouldn't need to repeat it all inside the loop. If you needed to do even more initialisation than fits nicely in the for statement's initialisation expression you can easily put it just before the for loop inside the while and it will all be re-run...
If you want to avoid jumps or the equivalent of goto statements that many of us have been trained to avoid, you could use a local function for the loop and a test on the return value to see if you should just call it again:
function doItAll() {
// put state variables other than the actual loop control here
function doTheLoop() {
for(var i=0; i<20; i++) {
if (somecondition) {
return(true); // run the loop again
}
}
return(false); // done running the loop
}
while (doTheLoop()) {}
// do some things after the loop
}
No. (Just to rule out a "I just haven't heard of it, either" - it isn't mentioned at https://developer.mozilla.org/en/JavaScript/Reference/Statements.)
continue works by simply skipping the rest of the loop body. break works by skipping the rest of the loop body and then ending the loop. A start function would have to somehow "rewind" the state of the program - but not all of the state of the program, since presumably you don't want to lose what you did, either - to where it was when the loop began, which is not something that any programming language that I have seen provides.
You could have the loop in a function that calls itself recursively:
function loopIt(numTimes) {
if (numTimes < 3) {
for (x = 0; x < 20; x++) {
if (x == 5) {
loopIt(numTimes+1);
break;
}
}
}
}
You can obviously change the conditions to fit you logic as the above is a simple example.