Javascript for loop and setTimeout issue - javascript

So I thought that the following code would be really simple but has become a big headache. It should be a loop that will change the opacity of and object so that it fades away.
function doSomething()
{
var i = 10;
for(i = 10; i >=0; i = i - 1)
{
setTimeout("setOpacity('t1',"+ i +")", 100*i);
WRITE 1
}
}
function setOpacity(elem, hmm)
{
WRITE 2
document.getElementById(elem).style.opacity = (10 - hmm)/10;
document.getElementById(elem).style.filter = 'alpha(opacity=' + (10 - hmm)*10 + ')';
}
So the problem is that the for loop is counting down from 10 to 0 and this has been confirmed by a print statement located at WRITE 1. However in the setOpacity method the numbers being received are starting at 0 and counting to 10 and this has been confirmed by a print statement at WRITE 2.
I would like to know why this is happening and how I can fix it. I believe it has something to do with the setTimeout call executing the method call after the end of the loop, but if that is so then why are the values being passed to setOpacity incrementing?
Any help is much appreciated.

The values being passed to setOpacity are increasing because you are passing different timeouts. The result of your loop is essentially the following:
setTimeout("setOpacity('t1', '10')", 1000)
setTimeout("setOpacity('t1', '9')", 900)
setTimeout("setOpacity('t1', '8')", 800)
....
setTimeout("setOpacity('t1', '0')", 0)
The result is that they are called in reverse order based on the timings. So the last call gets executed in 0ms (after the function finishes), resulting in 0 as hmm, followed by 1, 2, 3 ...
To fix this you need to change 100*i to 100 * (10 - i)

Related

JS learning exercise? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
So I'm just starting out teaching myself JS from a cheesy textbook and there's a challenge to figure out what this code does:
function clunk (times) {
var num = times;
while (num > 0) {
display("clunk");
num = num - 1;
}
}
function thingamajig (size) {
var facky = 1;
clunkCounter = 0;
if (size == 0) {
display("clank");
} else if (size == 1) {
display("thunk");
} else {
while (size > 1) {
facky = facky * size;
size = size - 1;
}
clunk (facky);
}
}
function display(output) {
console.log(output);
clunkCounter = clunkCounter + 1;
}
var clunkCounter = 0;
thingamajig(5);
console.log(clunkCounter);
For the life of me, I can't follow the logic here that gives the answer at the back, which is that "clunk" will display in the console 120 times. They mention it has to do with factorials (I was never any good with math...) but I can't find the step where the code to display "clunk" repeats that many times. To spare you all the details, every time I walk through it on paper/in my head I just get "clunk" 1 as the output in the console... can anyone hold my hand through this or show me where this factorial part is happening? Thanks!
I am not going to go through the code with you. But I am going to introduce you to a method in which you can go through the code with yourself, inspect it step by step, and understand it better.
What i am referring to is the use of debugging. By running the above code with node inspect script.js you can run the script in a debugging mode. This will allow you to place little stop signs within the script that will help you analyze what's going on as it is running.
The main stop sign is simply debugger.
When you put this in the code, the inspect mode will stop at that point and let you access the different variables and methods set up in the script from the console it self. That way you can track what's happening to the different elements and see step by step how the script is operating.
Analyzing it this way will help you understand the logic in a much more profound way and I highly recommend you give it a try.
Here is the script with the debugger set up in a decent way:
function clunk (times) {
var num = times;
while (num > 0) {
display("clunk");
num = num - 1;
debugger;
}
}
function thingamajig (size) {
var facky = 1;
clunkCounter = 0;
debugger;
if (size == 0) {
display("clank");
debugger;
} else if (size == 1) {
display("thunk");
debugger;
} else {
while (size > 1) {
facky = facky * size;
size = size - 1;
debugger;
}
clunk (facky);
}
}
function display(output) {
console.log(output);
clunkCounter = clunkCounter + 1;
debugger;
}
var clunkCounter = 0;
thingamajig(5);
console.log(clunkCounter);
Try it out!
Here are the docs about debugger.
PRO TIP:
You can literally just copy paste the code into your chrome console and it will start the chrome debugger running the script.
Because aviya.developer has already shared with you how to debug the program, I'll talk to you about the algorithm. I would suggest you study up on programming fundamentals a bit more, because it seems like you might now have a good grasp on how loops work. This algorithm relies on the variables facky, and size to do the factorial calculation, which we then pass to the display method. So you can repeat that for a few iterations and you should be able to start to understand the flow of the program. The factorial part is the while loop in the thingamajig method.
facky = facky * size;
facky is initialized to 1, and our size is 5.
The formula for a factorial can be found easily online:
n!=n×(n−1)×(n−2)...×2×1
5! = 5 * 4 * 3 * 2 * 1 = 120
Iteration 1: 1 * 5 = 5
Iteration 2: 5 * 4 = 20
Iteration 3: 20 * 3 = 60
Iteration 4: 60 * 2 = 120
Iteration 5: 120 * 1 = 120
This is the value that we will call the function clunk with.
clunk also has a while loop, which has a termination condition of num > 0. This means we will continue to iterate while this condition is true. Once it is no longer true, the iteration will stop.
This means, we will call the method displaywith a value of "clunk" 120 times before the iteration ends.
When you are analyzing an algorithm, it's important to figure out the key components/variables/operations of that algorithm, and keep track of how they change over time or through iterations. This will indicate when the end-goal is reached.

javascript is there a way to increment a for loop with a variable

I have a function -- let's call it test(arg1,arg2), called from program1, which does a number of things and is working correctly. Within test there is a loop:
for(j=1;j<=top;j++) {
stuff happens based on j
}
I would like to call test(arg1,arg2) from a different program, say program2. Everything about test is the same for these two programs except the for loop. For program2 I need that loop to be
for(j=2;j<=top;j+=2) {
stuff happens based on j
}
Otherwise everything else is exactly the same.
The second argument, arg2 tells us whether the script was called from program1 or program2. But I can't figure out how to write a variable "for" statement. I tried an if statement based on arg2
var jstart = 1 or 2
var jincr = '++' or '+=2'
and then wrote the loop as
for(j=jstart;j<=top;j jincr) {
This did not work, although it is an approach that works in other languages.
Can someone suggest I way I can do this without writing an entirely separate script for the two cases?
As simple as that
jstart = 1 // or 2
jincr = 1 // or 2;
for(j=jstart;j<=top;j += jincr) {
The most reusable way would be to put your loop in a function that accepts increment as an argument:
function doStuff (inc) {
for(var j = inc; j <= top; j += inc) {
// stuff happens based on j
}
}
// Program 1
doStuff(1)
// Program 2
doStuff(2)

Recursive function to print numbers in JavaScript

I'm trying to make a recursive function to print 1 to 10 in JavaScript, my current code is:
function rec10(x)
{
if (x < 10)
{
$('#text').val(x+1);
x = x+1;
rec10(x);
}
}
The problem is, everytime I activate this function, the text box only shows "10" directly, i want the code to move from 0 to 1, to 2... until 10. Showing each one of them in the text box.
I tried to use setInterval and setTimeout but I didn't figure it out how to work with that. Thank you very much
instead of:
rec10(x);
call
setTimeout(function() { rec10(x); }, 1000);
With setInterval you can using code below:
function rec10(x) {
var interval = setInterval(function() {
if (x >= 10) clearInterval(interval);
$('#text').val(x++);
}, 1000);
}
JavaScript is single threaded, which means while your code runs, the changes you make to the DOM won't be seen on the browser until your code finish.
You need to give control to the browser for couple of seconds, it can be done with setTimeout:
function rec10(x){
if (x < 10){
$('#text').val(++x);
setTimeout(function(){
rec10(x);
},20);
}
}
Did you resort to setInterval/setTimeout only because you can't make a working recursive function?
If that's the case, how about this recursive function below without using setInterval/setTimeout:
function rec10(x) {
if (x <= 10) {
if (x <= 0) x = 1;
//append?
$('#text').val(x);
rec10(x + 1);
}
}
rec10(1);
But the problem with the code above is it will happen so fast you won't notice the printing of numbers 1 up to 9.
If you want to see the those numbers then I suggest you just append the value to your placeholder $('#text').
But if you really wanna see the numbers being printed and then being replaced by the next number, then you can refer to the answers posted by other users which uses setInterval/setTimeout.

Why does console.log() keep on going in this loop?

I defined a variable 'i' to be equal to 2, and then say: if 'i' is between 0 and 2, let the function 'animate' run. However, if I open up the console in JSFiddle (option-command-I), the console.log() continues decreasing by 1 below 0! Am I using the conditional in the if statement improperly?
var interval = window.setInterval(animate, 500);
var i = 2;
if (0 < i < 2) {
function animate() {
alert('run');
i--;
console.log(i);
}
}
JSFiddle: http://jsfiddle.net/lpsternotes/RuLHn/
There are two problems here.
Firstly, there is this: 0<i<2. This will always evaluate to true.
Why? What you hoped it meant was "i is between 0 and 2" (which could be written as 0<i && i<2). But to a JS compiler, it is just two instances of the < operator. First, 0<i is evaluated, resulting in either true or false; then, that result is compared against <2 - so we have either true<2 or false<2. In order to answer that question, JS must "cast" the true or false to an integer. It treats false as 0, and true as 1; since these are both <2, the final result is always true.
Secondly, there is the position of your if statement, which is checked only once:
if (0 < i < 2) {
function animate() {
If you read through the code, you will see that there is no way of getting back to the line above this if statement, since the only repeating part of the code is the interval repeatedly running the animate function. To run each time the function runs, the if needs to be inside the function, not the other way around.
You need to clear interval once you satisfy the condition and move the if condition (note that 0 < i < 2 needs to be split into 2 conditions joined with && but here you can just do) with modification inside the function.
var interval = window.setInterval(animate, 500);
var i = 2;
function animate() {
if (i > 0) {
console.log('run');
i--;
console.log(i);
}
else{
window.clearInterval(interval);
}
}
Why don't you put if statement inside your animate function?
var i = 2;
function animate () {
if (i> 0 && i<2) {
alert('run');
i--;
console.log(i);
}
}
Edit:
1) you might want to use i>0 && i<2 rather than 0
2) Not sure if you do need to clear the interval after the condition is met. Would be better if you could elaborate a bit more about your requirements.

For Loop with dynamic numbers in timeout

I have a for loop that is running a timeout function that needs dynamic numbers but instead I'm getting the end result instead of the increments in some cases.
Here's my code (p.s. yes I realize setting 'start' variable inside for loop is not ideal):
var new_answer_start = 0;
var seconds = 0;
for (start=0; start<50; start++) {
new_answer_start = new_answer_start + 50;
seconds = seconds + 10000; //10 seconds
setTimeout(function(){reloadXMLDoc(xmlurl, new_answer_start);},seconds);
}
What I'm trying to accomplish is that increasing every 10 seconds the function will run with the new_answer_start equaling increments of 50. Instead I'm getting every 10 seconds the function is outputting each time starting at 2500 (the last set of numbers after 50 loops). I've had a similar issue before, something to do with closures and I used "let" to fix it then. However when I tried:
let new_answer_start = new_answer_start + 50;
I started getting NaN (not a number) errors. I thought it was weird that the timeout would increment in seconds (10, 20, 30, etc) but not the variable sent to the function. I'm assuming that's because once the function goes to run 10 seconds later, the variable increment has completed to the end. So how do I send the 'fixed' number to the function?
After doing research on similar questions here, I also tried putting the number into a closure:
setTimeout(function(){reloadXMLDoc(xmlurl, (new_answer_start));},seconds);
But this still start outputting at 2500.
Try using an actual closure:
setTimeout((function (new_answer_start) {
return function () {
reloadXMLDoc(xmlurl, new_answer_start);
};
})(new_answer_start), seconds);
window.new_answer_start = 0;
var interval = setInterval(function() {
window.new_answer_start = window.new_answer_start + 50;
}, 10000);

Categories

Resources