How to get the value for one function to another? - javascript

I'm learning Javascript, I read about scope and variables but can't get information about how to send variables between the functions.
Please can someone to explain how to be with this situation and it would be great to recommend something to read about it.
I want to draw a picture 30 times with 30 different parameters, and get the last parameter for checking function:
function loadImg{
.....
img.onload = function() { // Loading first picture at the start
........
boo.onclick = function() { // the function which by click start all process
var i = 0;
var num; // the variable which I'm going to use for random numbers.
setInterval(function() {
// here I'm generating random numbers
num = Math.floor(Math.random() * imgs.length);
// and then start draw() function, which is going to get the 'num' parameter and draw a pictures with time interval ONLY 30 times
if(i < 30){
draw(num);
i++; }, 2000);
check(num); // after all, I want to run "check()" function which is going to get THE LAST from that 30 generated 'num' parameter and check some information. But is undefined, because it's outside the setInterval function and I don't wont to put it in, because it will starts again and again.
How to get the last parameter for check(num) function?
P.S. Sorry for my english I've been trying to describe as good a as I can.

You could call check(num) inside the setInterval() function with a condition:
if(i < 30){
draw(num);
i++;
}
else
{
check(num);
}
You should also then end your loop as this will keep running indefinitely.
To do this assign the interval to a variable:
var myInterval = setInterval(function() {
And then clear the interval before calling check():
if(i < 30){
draw(num);
i++;
}
else
{
clearInterval(myInterval);
check(num);
}

Related

setInterval go faster every time it is called

This time go faster if is called 2 times, 3 times faster and so on.
function startIdleTime() {
var ciclo;
function startSlidercicle() {
ciclo = setInterval( function() {
let seconds = parseInt(sessionStorage.getItem('idle'));
seconds += 1;
sessionStorage.setItem('idle', seconds);
}, 1000);
}
// start idle counter
if (!sessionStorage.getItem('idle')) {
alert('non esiste timer, lo imposto')
sessionStorage.setItem('idle', 0);
alert(3)
}
if (sessionStorage.getItem('idle') > 15) {
anotherFunction();
}
if (sessionStorage.getItem('idle') < 15 || !sessionStorage.getItem('idle')) {
clearInterval(ciclo);
startSlidercicle();
}
}
I need to set idle time. When 15 i'll call an other function,
if <= 15 I reset only a counter to 0
But if is called few times my count go faster then 1sec }, 1000);)
Looks like the interval is not cleared before instantiating a new one. The result will be several intervals that will be executed with a different phase, and it will look like it's running with a shorter interval.
The reason for this behavior is that you are not clearing the interval, because you are creating a new ciclo variable every time you call the startIdleTime function. Probably you want the variable ciclo to be global, in order to share the interval handler across the function calls. You need to widen the scope, and to widen the scope you can just move the variable declaration out of the function definition:
var ciclo;
function startIdleTime() {
function startSlidercicle() {
Also note that in the following line:
sessionStorage.getItem('idle') < 15 || !sessionStorage.getItem('idle') 
the second part is never evaluated. Let's suppose that getItem returns null: then null < 15 is true, so the check becomes useless

How to stop a function when it's called?

I'm building a program that either counts down or up and I've got it working however I like to press count-up in the middle of count down or vice versa and I like the counter to stop and count up or vice versa. how do I achieve that? thanks a lot for your help :)
function myFunctionUp() {
var Timer = setInterval(function () {
i++;
document.getElementById("mydata").textContent = i;
if (i >= 21)
clearInterval(Timer);
if (i == 21){
document.getElementById("mydata").textContent = "Boom-up!";
}
}, 1000);
}
function myFunctionDown() {
var Timer = setInterval(function () {
i--;
document.getElementById("mydata").textContent = i;
if (i <= 0)
clearInterval(Timer);
if (i == 0){
document.getElementById("mydata").textContent = "Boom-down";
}
}, 1000);
}
Use a variable to keep track of the way to count. When a button is clicked, invert the value of the variable :
let countDown = 10;
let increment = -1;
function count() {
countDown += increment;
document.getElementById('container').innerText = countDown;
setTimeout(() => count(), 1000);
}
document.getElementById('btn').addEventListener('click', function () {
increment = -increment;
});
count();
Working stackblitz here
You typically never "take control" on the execution of another method. When you want to do that, the logic must be inverted. The function itself must ask if it should continue.
With an example : let's take a function which works in an infinite loop, that you want to be able to stop on demand. In some languages, you could run some code in a thread and interrupt the thread on demand. But even if it is possible, it is generally a bad idea to stop some code at the middle of its execution.
A better way of doing that is to create a "should Continue ?" piece of code at the end of the iteration. It could read a variable or call a method etc. When you want to stop the iteration, you just have to set this variable and you know that the infinite loop will stop graciously at the end of the current iteration

Having a hard time with callbacks

Learning about callbacks and the solution to this problem eludes me.
It should print a number every second counting down until zero. Currently, it logs the numbers from 10 - 0 but all at once and continues in an infinite loop.
Please help me to gain a better understanding of this situation. I have read up on callbacks and have a conceptual understanding but execution is still a bit tricky.
var seconds = 0;
var countDown = function(){
for(var cnt = 10; cnt > 0; cnt--){
setTimeout(function(x){
return function(){
seconds++
console.log(x);
};
}(cnt), seconds * 1000);
}
}
countDown()
The way your code is working now, it executes a for loop with cnt going from 10 to 1. This works. On each iteration, it schedules a new function to be run in seconds * 1000 milliseconds, carefully and properly isolating the value of cnt in x each time. The problem is that seconds is 0, and it will only be changed once a callback executes; but by the time a callback executes, all of them will already have been scheduled for execution. If you need seconds * 1000 to vary while you’re still scheduling them all (while the for loop is still running), you need to change seconds in the loop, rather than inside one of the callbacks.
Read up on IIFEs to see how they work. In this situation, you're creating a closure of the value you want to print. You had the right idea, but your syntax was off.
var seconds = 0;
var countDown = function () {
var cnt = 10;
// simplify your loop
while (cnt--) {
// setTimeout expects a function
// use an IIFE to capture the current value to log
setTimeout((function (x) {
// return the function that setTimeout will execute
return function (){
console.log(x + 1);
};
}(cnt)), (++seconds) * 1000);
}
};
countDown();

Why setInterval wont stop?

Why console.log(1) gets executed here forever:
var interval = setInterval(function() {
if (true) {
clearInterval(interval);
console.log(1);
}
}, 100);
It depends on the scope within which you're executing this code.
If interval is unique within its scope — be it global or function scope — then this will work as expected.
If, however, you execute this code within a loop (for example), then you are overwriting interval with some new interval on each iteration, breaking your clearInterval call for all but the very last setInterval call:
for (var i = 0; i < 3; i++) {
var interval = setInterval(function() {
if (true) {
clearInterval(interval);
console.log(1);
}
}, 100);
}
// ^ will give you one single console log entry,
// and two more console log entries per second forever
It's seems that your variable interval is used somewhere again. If I run code you provided it works as expected. I guess user Lightness has given a great explaination of this, also he provided piece of code where "closure problem" is obvious (which caused you problem too). I just want to add extra information. If you want your code inside of loop + setInteval works aparat you can do the following:
for (var i = 0; i < 3; i++) {
var o = {
i: i,
interval: null,
timer: function() {
if (true) {
clearInterval(this.interval);
console.log(this.i);
}
}
};
o.interval = setInterval(o.timer.bind(o), 1000);
}
DEMO
I hope it will be useful for someone.

setTimeout in for-loop does not print consecutive values [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
I have this script:
for (var i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
But 3 is alerted both times, instead of 1 then 2.
Is there a way to pass i, without writing the function as a string?
You have to arrange for a distinct copy of "i" to be present for each of the timeout functions.
function doSetTimeout(i) {
setTimeout(function() {
alert(i);
}, 100);
}
for (var i = 1; i <= 2; ++i)
doSetTimeout(i);
If you don't do something like this (and there are other variations on this same idea), then each of the timer handler functions will share the same variable "i". When the loop is finished, what's the value of "i"? It's 3! By using an intermediating function, a copy of the value of the variable is made. Since the timeout handler is created in the context of that copy, it has its own private "i" to use.
Edit:
There have been a couple of comments over time in which some confusion was evident over the fact that setting up a few timeouts causes the handlers to all fire at the same time. It's important to understand that the process of setting up the timer — the calls to setTimeout() — take almost no time at all. That is, telling the system, "Please call this function after 1000 milliseconds" will return almost immediately, as the process of installing the timeout request in the timer queue is very fast.
Thus, if a succession of timeout requests is made, as is the case in the code in the OP and in my answer, and the time delay value is the same for each one, then once that amount of time has elapsed all the timer handlers will be called one after another in rapid succession.
If what you need is for the handlers to be called at intervals, you can either use setInterval(), which is called exactly like setTimeout() but which will fire more than once after repeated delays of the requested amount, or instead you can establish the timeouts and multiply the time value by your iteration counter. That is, to modify my example code:
function doScaledTimeout(i) {
setTimeout(function() {
alert(I);
}, i * 5000);
}
(With a 100 millisecond timeout, the effect won't be very obvious, so I bumped the number up to 5000.) The value of i is multiplied by the base delay value, so calling that 5 times in a loop will result in delays of 5 seconds, 10 seconds, 15 seconds, 20 seconds, and 25 seconds.
Update
Here in 2018, there is a simpler alternative. With the new ability to declare variables in scopes more narrow than functions, the original code would work if so modified:
for (let i = 1; i <= 2; i++) {
setTimeout(function() {
alert(i)
}, 100);
}
The let declaration, unlike var, will itself cause there to be a distinct i for each iteration of the loop.
You can use an immediately-invoked function expression (IIFE) to create a closure around setTimeout:
for (var i = 1; i <= 3; i++) {
(function(index) {
setTimeout(function() { alert(index); }, i * 1000);
})(i);
}
This's Because!
The timeout function
callbacks are all running well after the completion of the loop. In fact,
as timers go, even if it was setTimeout(.., 0) on each iteration, all
those function callbacks would still run strictly after the completion
of the loop, that's why 3 was reflected!
all two of those functions, though they are defined
separately in each loop iteration, are closed over the same shared global
scope, which has, in fact, only one i in it.
the Solution's declaring a single scope for each iteration by using a self-function executed(anonymous one or better IIFE) and having a copy of i in it, like this:
for (var i = 1; i <= 2; i++) {
(function(){
var j = i;
setTimeout(function() { console.log(j) }, 100);
})();
}
the cleaner one would be
for (var i = 1; i <= 2; i++) {
(function(i){
setTimeout(function() { console.log(i) }, 100);
})(i);
}
The use of an IIFE(self-executed function) inside each iteration created a new scope for each
iteration, which gave our timeout function callbacks the opportunity
to close over a new scope for each iteration, one which had a variable
with the right per-iteration value in it for us to access.
The function argument to setTimeout is closing over the loop variable. The loop finishes before the first timeout and displays the current value of i, which is 3.
Because JavaScript variables only have function scope, the solution is to pass the loop variable to a function that sets the timeout. You can declare and call such a function like this:
for (var i = 1; i <= 2; i++) {
(function (x) {
setTimeout(function () { alert(x); }, 100);
})(i);
}
You can use the extra arguments to setTimeout to pass parameters to the callback function.
for (var i = 1; i <= 2; i++) {
setTimeout(function(j) { alert(j) }, 100, i);
}
Note: This doesn't work on IE9 and below browsers.
ANSWER?
I'm using it for an animation for adding items to a cart - a cart icon floats to the cart area from the product "add" button, when clicked:
function addCartItem(opts) {
for (var i=0; i<opts.qty; i++) {
setTimeout(function() {
console.log('ADDED ONE!');
}, 1000*i);
}
};
NOTE the duration is in unit times n epocs.
So starting at the the click moment, the animations start epoc (of EACH animation) is the product of each one-second-unit multiplied by the number of items.
epoc: https://en.wikipedia.org/wiki/Epoch_(reference_date)
Hope this helps!
You could use bind method
for (var i = 1, j = 1; i <= 3; i++, j++) {
setTimeout(function() {
alert(this);
}.bind(i), j * 100);
}
Well, another working solution based on Cody's answer but a little more general can be something like this:
function timedAlert(msg, timing){
setTimeout(function(){
alert(msg);
}, timing);
}
function yourFunction(time, counter){
for (var i = 1; i <= counter; i++) {
var msg = i, timing = i * time * 1000; //this is in seconds
timedAlert (msg, timing);
};
}
yourFunction(timeInSeconds, counter); // well here are the values of your choice.
I had the same problem once this is how I solved it.
Suppose I want 12 delays with an interval of 2 secs
function animate(i){
myVar=setTimeout(function(){
alert(i);
if(i==12){
clearTimeout(myVar);
return;
}
animate(i+1)
},2000)
}
var i=1; //i is the start point 1 to 12 that is
animate(i); //1,2,3,4..12 will be alerted with 2 sec delay
the real solution is here, but you need to be familiar with PHP programing language.
you must mix PHP and JAVASCRIPT orders in order to reach to your purpose.
pay attention to this :
<?php
for($i=1;$i<=3;$i++){
echo "<script language='javascript' >
setTimeout(function(){alert('".$i."');},3000);
</script>";
}
?>
It exactly does what you want, but be careful about how to make ralation between
PHP variables and JAVASCRIPT ones.

Categories