keep for loop from executing in Javascript - javascript

Currently for loop gets executed till the end even though the function it calls hasn't finished executing. I want to make it such that, when startloop function is called until it is executed completely for loop shouldn't move forward.. How to do that? I tried simulating goto but it didn't work..
Here's my code:
function startLoop(i) {
console.log("startloop function start");
var centerX = xObj[i];
var centerY = yObj[i];
var radius = 10;
var alpha = 1, /// current alpha value
delta = 0.01; /// delta = speed
var flag = 0;
var num = 0
function loop() {
console.log("inside loop " + centerX + " " + centerY);
alpha -= delta;
if (alpha <= 0) {
//console.log("heya_amigoes");
num = 2;
return;
}
//console.log("hi1");
/// clear canvas, set alpha and re-draw image
ctx2.clearRect(0, 0, 1000, 600);
ctx2.globalAlpha = alpha;
ctx2.beginPath();
ctx2.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
ctx2.fillStyle = "#FF0000";
ctx2.fill();
ctx2.lineWidth = 1;
ctx2.strokeStyle = '#003300';
ctx2.stroke();
//console.log("hi2");
//requestAnimationFrame(loop); // or use setTimeout(loop, 16) in older browsers
setTimeout(loop, 16)
console.log("Outside function loop");
}
loop();
/*
LABEL1: do {
if(num==2)
{
num=0;
break LABEL1;
}
if(num!=2)
continue LABEL1;
}while(1);
*/
console.log("startloop function stop");
}
for (i = 0; i < xObj.length; i++) {
console.log("for loop running " + i);
startLoop(i);
console.log("outside loop func");
}

A for loop will not wait for your task. To achieve this task, you will have to use recursion.
Logic:
Call a function and pass a callback in it.
On execution completion, run passed callback.
Now since we have to chain same function again and again, pass same callback to next iteration again and have a check(threshold limit) and stop on breach.
var count = 0
function test1(i, cb){
console.log("In Test " + i)
if(i < 10)
setTimeout(cb.bind(null, ++count, cb), 2000)
}
test1(count, test1)
Explanation:
My approach mimics behaviour of a do..while loop. It has 4 parts:
Initialisation: This will initialise the iterator variable. This variable will be use to check for termination condition and to check the current iterator's value. In my Case: count = 0
Execution of Code block: This will execute the code defined inside {...}. In my case: test1(count, test1)
Termination condition: This check if next iteration should be performed or not? In my case: if(i<10). If condition is satisfied, start next iteration: setTimeout(cb.bind(null, ++count, cb), 2000)
Increment Value: This updated value of iterator to point to next value. In my case: ++count
This is the final JSFiddle

I think that the problem going to be with your for loop. startLoop function always going to get the xObj.length-1.
You can read more about this problem: JavaScript closure inside loops – simple practical example
The variable i, within each of your anonymous functions, is bound to
the same variable outside of the function.
Solution with ES6 let feature:
for (let i = 0; i < xObj.length; i++) {
console.log("for loop running " + i);
startLoop(i);
console.log("outside loop func");
}
Solution without ES6:
var funcs = [];
for (var i = 0; i < xObj.length; i++) {
funcs[i] = startLoop(i);
}
for (var i = 0; i < xObj.length; i++) {
console.log("for loop running " + i);
funcs[i]();
console.log("outside loop func");
}

Related

Running a jQuery function multiple times sequentially (for a Bookmarklet)

I've got the following jQuery code which I use in a Bookmarklet. It clicks on all the buttons on the page (with the class "Unfollow") one by one, with a random time between each one...
javascript: (function() {
var unfollowButtons = $('button.Unfollow');
var index = unfollowButtons.length - 1;
unfollow();
function unfollow() {
if (index >= 0) {
$(unfollowButtons[index--])
.click();
setTimeout(unfollow, Math.floor((Math.random() * 1000) + 500));
}
}
})();
I'd like to run the above function again twice once it has completed its cycle.
Just running the function again causes that to run in parallel with the first function call.
How do I run the unfollow() function 2 or 3 times without them all running in parallel?
Try it this way (using ES6 Promises):
var runUnfollow = function() {
return new Promise(function(resolve, reject){
var index = unfollowButtons.length - 1;
// fencepost for the loop
var p = Promise.resolve();
// we stop execution at `i == 0`
for (var i = index; i >= 0; i--) {
// run the promise
// then set `p` as the next one
p = p.then(unfollowTimeout.bind(null, i));
}
// make sure we run the last execution at `i == 0`.
p.then(function(){
resolve();
})
function unfollowTimeout(i){
// return a promise to run `unfollow` and a `setTimeout`
return new Promise(function(resolve,reject){
unfollow(i);
setTimeout(resolve, Math.floor((Math.random() * 1000) + 500));
})
}
function unfollow(i) {
$(unfollowButtons[i])
.click();
}
})
}
// run three times synchronously
runUnfollow().then(runUnfollow).then(runUnfollow).then(function(){
//finished
});
// another way to run three times synchronously
p = runUnfollow();
for(i=3; i > 0; i--){
p = p.then(runUnfollow);
}
p.then(function(){
//finished
});
// run in parallel
Promise.all([runUnfollow, runUnfollow, runUnfollow])
.then(function(){
//finished
});
EDIT: Went back and read your question again, realized you were trying to run everything multiple times. I've edited to reflect that.
Just reset index and restart after each button is clicked:
javascript: (function() {
var unfollowButtons = $('button.Unfollow');
var index = unfollowButtons.length - 1;
var totalRuns = 3;
unfollow();
function unfollow() {
if (index < 0 && totalRuns) {
totalRuns--;
unfollowButtons = $('button.Unfollow');
index = unfollowButtons.length - 1;
}
if (index >= 0) {
$(unfollowButtons[index--])
.click();
setTimeout(unfollow, Math.floor((Math.random() * 1000) + 500));
}
}
})();
You should look at Promises.
Resolve your Promise at your function's execution's very end and call your function again. You should be good with that.
In your specific case, you could simply build an array which contains twice each button :
// turn the jQuery selection into a regular array :
var unfollowButtons = $('button.Unfollow').get();
// build a new array, which contains two copies of the above selection :
unfollowButtons = unfollowButtons.concat(unfollowButtons);
You have 2 options
1.Use a User Script
For that, you need a User Script extension manager, for example Tampermonkey for Chrome, Greasemonkey for Firefox, etc.
But since you want a bookmarklet, just leave it.
2.Modify the Bookmarklet a little as follows
Add this code inside the unfollow function
That is check whether index reached 0 and also the flag is set or not.
FLAG is important otherwise it will create a infinitive recursion loop.
First set FLAG to 0 outside of unfollow function.
Then in unfollow function, if the FLAG is 0 and index is 0, initiate the next iteration and Set FLAG to 1.
if(index < 0 && FLAG==0){
var unfollowButtons = $('button.Unfollow');
FLAG=1;
var index = unfollowButtons.length - 1;
unfollow();
}
So, it will look like this.
javascript: (function() {
var unfollowButtons = $('button.Unfollow');
var index = unfollowButtons.length - 1;
var FLAG=0;
unfollow();
function unfollow() {
if (index >= 0) {
$(unfollowButtons[index--])
.click();
if(index < 0 && FLAG==0){
unfollowButtons = $('button.Unfollow');
FLAG=1;
index = unfollowButtons.length - 1;
unfollow();
}
setTimeout(unfollow, Math.floor((Math.random() * 1000) + 500));
}
}
})();
If you want to do it totally 3 times, change if(index < 0 && FLAG<=2){ and FLAG=1 to FLAG +=1
As i understand your requirements it can be done like this :
javascript: (function() {
var unfollowButtons = $('button.Unfollow');
var index = unfollowButtons.length - 1;
unfollow();
var runForNoOfTime = 3;
var runningForTime = 1;
function unfollow() {
if (index >= 0) {
$(unfollowButtons[index--]).click();
setTimeout(unfollow, Math.floor(Math.random() * 1000) + 500));
}else if(runningForTime < runForNoOfTime){
runningForTime++;
unfollowButtons = $('button.Unfollow'); //if buttons with class 'Unfollow' changes.
index = unfollowButtons.length - 1;
setTimeout(unfollow, Math.floor(Math.random() * 1000) + 500));
}
}
})();
You can use recursion to achieve the desired effect of running your function multiple times sequentially. Here's how this can be done:
(function() {
var unfollowButtons = $('button.Unfollow');
var index = unfollowButtons.length - 1;
function unfollow(callback) {
if (index >= 0) {
$(unfollowButtons[index--]).click();
}
callback();
}
function handleUnfollow(maxIter, iter) {
iter = typeof iter === "number" ? iter : 0;
if ( iter >= maxIter ) {
// base case reached, stop further recursive calls
return true;
}
// call unfollow once
unfollow(function() {
setTimeout(function() {
// recursive call
handleUnfollow(maxIter, ++iter);
}, Math.floor((Math.random() * 1000) + 500));
});
}
// execute recursive function, which will iterate 2 times
handleUnfollow(2);
})();
As far as I'm aware Javascript runs on a single thread, so there is no actual parallel processing taking place.
If you just simply want the function to run itself x times then use recursion:
function DoSomething(steps) {
// Do stuff here
steps--;
if (steps <== 0) {
return;
}
DoSomething(steps);
}
If you want things to run in "parallel" with Javascript then perhaps you could look into having some external code that manages threads and executes multiple Javascript processes in parallel (although I'm not sure if this is possible, and, if it is, whether you'll be able to have the scripts accessing the same data at the same time or talking to eachother).
I have made the current code as block and added wrapper logic. Check if this works.
(function() {
var iterations = 2;
unfollowBlock();
function unFollowBlock() {
if (iterations-- >0) {
var unfollowButtons = $('button.Unfollow');
var index = unfollowButtons.length - 1;
unfollow();
function unfollow() {
if (index >= 0) {
$(unfollowButtons[index--])
.click();
setTimeout(unfollow, Math.floor((Math.random() * 1000) + 500));
}
else { //index=-1 end of unfollowblock
setTimeout(unFollowBlock, Math.floor((Math.random() * 1000) + 500));
}
}
}
}
})();
Declare a flag/check variable eg. var isCycleComplete;
var isCycleComplete = false;
if(isCycleComplete){ // if true runs unfollow function twice
unfollow();
unfollow();
isCycleComplete = false; // resets flag
}
else{ // if false
unfollow();
isCycleComplete = true; //sets flag to true
}
M not a pro at javascript but See if this simple snippet helps you.

Changing speed of console.log in loop

I want to slow down console.log in my loop
// function update to actualize value
function update() {
requestAnimationFrame(update);
analyser.getByteFrequencyData(data);
var count=0;
for (var i=data.length; i--;) {
count+=data[i];
if(count >= 1) {
console.log(data);
}
};
}
For example, show one console.log immediatly, and then, each .5s
How can we do that ?
(maybe with setTimeout() but I don't want start delay)
The simplest way would be to introduce a timeout before running the update again each time...
// function update to actualize value
function update() {
analyser.getByteFrequencyData(data);
var count=0;
for (var i=data.length; i--;) {
count+=data[i];
if(count >= 1) {
console.log(data);
}
};
requestAnimationFrame(function() {
setTimeout(update, 5000);
});
}
I used setTimeout() in preference over setInterval() as doing it this way (as well as moving the call to the end of the function) will make sure everything is completed, before starting the 5 second pause. It ensures there's no overlap, should the preceeding code take longer than 5 seconds.
First create a variable to store the time of the last console.log. Next, update that variable each time you console.log a value. Finally, add a check for the threshold.
var lastOutput = 0; // Setting this to 0 initially will ensure it runs immediately
var outputThreshold = 500; // in milliseconds
function update() {
requestAnimationFrame(update);
analyser.getByteFrequencyData(data);
if (new Date().valueOf() - lastOutput > outputThreshold) {
// threshold met, output and update
var count=0;
for (var i=data.length; i--;) {
count+=data[i];
if(count >= 1) {
console.log(data);
}
};
lastOutput = new Date().valueOf();
}
}
update(); // fire first call to update, after that requestAnimationFrame() will handle future calls
If you want the time delay inside the for loop, you'd do this:
function update() {
requestAnimationFrame(update);
analyser.getByteFrequencyData(data);
var i = data.length - 1, count = 0;
function logger() {
count += data[i];
if (count >= 1)
console.log(data);
if (i-- >= 0)
setTimeout(logger, 500);
}
logger();
}
Now, things are going to be pretty messy because you're also using requestAnimationFrame() to schedule another iteration of the whole thing; that really won't make sense anymore. You'll probably want to have that wait until the logging process is done:
function update() {
analyser.getByteFrequencyData(data);
var i = data.length - 1, count = 0;
function logger() {
count += data[i];
if (count >= 1)
console.log(data);
if (i-- >= 0)
setTimeout(logger, 500);
else
requestAnimationFrame(update);
}
logger();
}

For Loop Delay Without setTimeout

So, here's my problem... I've got a for loop inside a function
var fan1 = function () {
for(var i=0; i<flare1base.length; i++) {
for(var i=0; i<200; i++)
flare1base.rotation.z += 0.01;
};
};
It should do essentially that, fairly simple, but... I need it to wait 10 milliseconds before adding to the rotation again. The problem is I don't think I can use setTimeout or just use setInterval instead of the entire for loop, because it's acting on an object in an array, and if I do
f1 = setInterval("flare1array[i].rotation.z += 0.01",10);
setTimeout("clearInterval(f1)",2000);
It queues up an action to do rotate the thingy, but by the time the action occurs the for loop has gone around again and "i" is different.
See jQuery: Wait/Delay 1 second without executing code
Have you tried:
setTimeout(function (){
flare1array[i].rotation.z += 0.01;
}, 10);
How about using setInterval and then clearInterval? You could do something like this:
function rotate(max) {
var i = 0;
return function () {
flare1base.rotation.z += 0.01;
if (++i == max) clearInterval(id);
}
}
var id = setInterval(rotate(200), 10);
Similar example on JSFiddle
setInterval returns an id that can later be passed to clearInterval to prevent the action from continuing indefinitely. Here I've wrapped a counter variable i in a closure, which keeps track of how many times the inner function has been called. When it has been called max times, it stops.
By the way, if you know what the starting value of flare1base.rotation.z is, it would be better to calculate it afresh each time the function is called rather than continuously adding 0.01, as the result of repeated floating point additions may be imprecise. For example, if you know it starts at, you could do flare1base.rotation.z = 0.01 * ++i; (and remove the increment from the if statement).
To extend this to an array of items, you can wrap the whole process in a loop. Assuming that your items are in an array arr:
function rotate(arr, idx, max) {
var i = 0;
return function () {
arr[idx] += 0.01;
if (++i == max) clearInterval(ids[idx]);
}
}
var ids = new Array(5);
for (var i = 0; i < 5; ++i) {
ids[i] = setInterval(rotate(arr, i, 200), 10);
}
updated JSFiddle

Delay javascript code execution inside for loop

I have the following for loop:
for (var i = tileLog.length - 1; i >= 0; i--) {
$('.' + tileLog[i]).mouseenter();
};
1 < tileLog.legth < 1025
Is there a way to delay each iteration of the loop so that mouseenter() is triggered every x miliseconds?
I have tried:
function doSetTimeout(i) {
setTimeout(function() { $('.' + i).mouseenter(); }, 250);
}
for (var i = tileLog.length - 1; i >= 0; i--)
doSetTimeout(tileLog[i]);
This doesn't seem to work, it just delays by 250ms then iterates through the loop
As an alternative to using setTimeout() you could also use setInterval().
Define a running variable in the outer scope (like your running i in the loop).
In each iteration, besides calling your function, decrement the running variable. If it is below zero, stop the setInterval()`` :
var index = tileLog.length - 1,
timer = setInterval( function(){
$('.' + tileLog[index]).mouseenter();
index -= 1;
if ( index < 0 ) {
clearInterval( timer );
}
}, 250 );
There is no actual sleep() function or something similar. Would also be problematic as JavaScript (for most cases) is single threaded and such a method would block the render thread, thus rendering your browser inaccessible.
There is no sleep or such in JavaScript. So your approach with timeout is correct.
var tileLog;
var i = titleLog.length - 1;
function func1() {
$('.' + tileLog[i]).mouseenter();
if (--i) {
window.setTimeout(func1, 250);
}
}
// and of course start the process
func1();

Using setTimeout func. & random func. to animate class elements

My debugging skills are not helping me much with figuring out what I am doing wrong here.
I want each element in an array to animate after a specified time using setTimeout function.
I am not getting any errors and the loop appears to run just fine, however, none of the elements in the array end up moving from their original place to the new spot.
function publicity()
{
// placing elements with class name 'cCameras' inside an array
var eCamerasArray = $(".cCameras").toArray();
// creating 2 arrays to hold left & top values of each element
var iLeftPosArray = [];
var iTopPosArray = [];
// loop to run through each element in array
for( var i = 0; i < eCamerasArray.length; i++)
{
// timer variable set for each element to be used in setTimeout func.
var timer = Math.floor (Math.random()*300) + 100;
// setTimeout func. used to animate each element after a specified (timer) time
window.setTimeout (function ()
{
iLeftPosArray[i] = Math.floor (Math.random() *139) + 360;
iTopPosArray[i] = Math.floor (Math.random() *160) + 100 ;
$(eCamerasArray[i]).animate ({left: iLeftPosArray[i] + "px", top: iTopPosArray[i] + "px"}, 100, "linear");
return [iLeftPosArray[i], iTopPosArray[i]];
}, timer);
}
}
You can fix it with creating closure:
(function publicity() {
var eCamerasArray = $(".cCameras"),
iLeftPosArray = [],
iTopPosArray = [],
timer;
for(var i = 0; i < eCamerasArray.length; i += 1) {
timer = Math.floor (Math.random() * 300) + 100;
(function (i) {
window.setTimeout (function () {
iLeftPosArray[i] = Math.floor (Math.random() * 139) + 360;
iTopPosArray[i] = Math.floor (Math.random() * 160) + 100 ;
$(eCamerasArray[i]).animate ({left: iLeftPosArray[i] + "px", top: iTopPosArray[i] + "px"}, 300, "linear");
return [iLeftPosArray[i], iTopPosArray[i]];
}, timer);
}(i));
}
}());
You can see the effect here: http://jsfiddle.net/zHUAt/2/
Best regards!
Unrolling a simple loop you can see what happens:
var i = 0;
window.setTimeout( function(){
//No local i so it must be outside
console.log(i);
}, 1000 );
i++;
window.setTimeout( function(){
//No local i so it must be outside
console.log(i);
}, 1000 );
i++;
window.setTimeout( function(){
//No local i so it must be outside
console.log(i);
}, 1000 );
As you can see, all the functions refer to the same i, so
they will all log 2 once the timers fire. None of them
have a local i.
You can create a "local" i like this:
(function(i){
|---------^ //i found here, no need to use the global i
| window.setTimeout( function(){
-------------------- //no local i here so it must be outside
console.log(i);
}, 1000 );
})(i) //pass the "global" i as argument, with the value it has right now

Categories

Resources