Having a hard time with callbacks - javascript

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();

Related

setInterval in function keeps speeding and clear interval is ignored

When I activate showMoves() the setInterval is supposed to repeat the function one second at a time.
However, it speeds up after a few seconds and activates the function several times in a single second instead of once. Also the clearInterval isn't working even though the if statement for it turns true.
let i = -1;
function showMoves() {
const start = setInterval(showMoves, 1000);
if (i > game.computerMoves.length) {
clearInterval(start);
}
console.log(i + ' ' + game.computerMoves.length);
const showColors = new Map([
[green, 'lime'],
[yellow, 'rgb(255,255,102)'],
[blue, 'dodgerblue'],
[red, 'salmon'],
]);
i++;
let move = game.computerMoves[i];
move.style.backgroundColor = showColors.get(move);
}
You shouldn't be calling setInterval inside the very same function that's being called by setInterval -- you're setting up multiple interval timers that way, not just one that you can clear away easily. It's "speeding up" as you say because you're seeing more and more different interval timers calling the same function.
You're recursively calling showMoves - every second, a new interval is created. When the function runs and the stop condition isn't reached, the start reference you have to the interval you just created is just garbage collected. You want a method to save the intervals so that you can reference them later so they can be cleared. For example:
let intervals = [];
function showMoves() {
intervals.push(setInterval(showMoves, 1000));
if (i > game.computerMoves.length) {
// clear all currently running intervals:
intervals.forEach(clearInterval);
intervals = [];
}
// ...

JavaScript setTimeout in for Loop for Performance Test

I am trying to use setTimeout for a performance test using console.time() and console.timeEnd(). However, setTimeout() isn't delaying the for() loop.
Here is the JS Fiddle:
// Start the timer.
console.time('testFor');
for ( i = 0; i < 5; i++ ) {
setTimeout( console.log('The number is ' + i), 2000 );
}
// End the timer, get the elapsed time.
console.timeEnd('testFor');
How can I make setTimeout() work in my script and why is it not working for me?
Others have already suggested excellent solutions to your problem - I'd like to offer a closer examination of what was happening with your code.
setTimeout doesn't delay the advancement of your code (like sleep does in other languages) but instead defers running another block of code. It's a weird concept that can be hard to understand; consider the following example:
console.time("Main Program");
console.time("Defered Code");
setTimeout(deferedCode, 1000);
console.timeEnd("Main Program");
function deferedCode() {
console.timeEnd("Defered Code");
}
If we look at the console after running this code, we'll see "Main Program" print out almost immediately, and "Defered Code" roughly a second later.
We've asked setTimeout to run the function deferedCode at a time 1000 ms in the future, and then let the rest of the code run.
Notice also that deferedCode is passed to setTimeout without (). We're passing the function itself and not the result of deferedCode to setTimeout so that setTimeout can choose when to run it.
You could do this without a loop, just using recursion:
var i = 0;
var logNumber = function(){
console.log('The number is ' + i);
i++;
if(i < 5){
setTimeout(logNumber, 2000);
}
}
logNumber();
setTimeout is asynchronous. If you want to call a certain piece of code at regular intervals, use the setInterval function.
I think this code is close to what you are trying to do:
// Start the timer.
console.time('testFor');
var i = 0;
var finish = 5;
var t = setInterval(function() {
if (i < finish) {
console.log('The number is ' + i);
i++;
} else {
clearInterval(t);
// End the timer, get the elapsed time.
console.timeEnd('testFor');
}
}, 2000);

setTimeout not waiting specified time

My setTimeout statements are making function calls instantly instead of waiting the time I specified and I'm not sure why. It should take 10 seconds for the for loop and then 110 seconds to change a boolean value from T to F.
for(var c = 0; c < 10; c++)
{
setTimeout(function()
{
gold = gold + clickerPower;
$("#totalGold").html("Gold: " + gold);
console.log("Clicked!");
}, 1000);
}
setTimeout(function(){frenzyActive = false;}, 110000);
Starting a timeout is an asynchronous operation. setTimeout accepts a function as it's first argument because it's registering that callback function to be invoked at a later time. Every iteration of the JavaScript event loop it checks to see if the appropriate time has passed and if so then it fires the registered callback. Meanwhile it's still moving on to run other code while it waits.
Your for loop is not waiting for anything. It iterates to 10 super fast and all you've done is register ten callbacks to fire all at the same time in exactly one second (the time is specified in milliseconds by the way, so 1000 = 1 second).
You need setInterval.
var count = 0;
var intervalId = setInterval(function () {
gold = gold + clickerPower;
$('#totalGold').html('Gold: ' + gold);
console.log('Clicked!');
count++;
if (count === 10) {
clearInterval(intervalId);
frenzyActive = false;
}
}, 1000);
That function will run once every second and increment a count variable each time. When it reaches 10 we call clearInterval and give it the intervalId returned from setInterval. This will stop the interval from continuing.
Take a gander at this post I wrote back when I too was confused about asynchronous callbacks :)
http://codetunnel.io/what-are-callbacks-and-promises/
I hope that helps :)
Good luck!
It will not take 10 seconds to execute the loop. Look will execute immediately and the anonymous function will be enqueued 10 times to be executed after 1 second.
The last call to setTimeout will cause the anonymous function to be executed after 110 seconds.
To ensure that the anonymous function within the loop is called sequentially, 10 times, after a gap of 1 second each, do the following:
var c = 0;
setTimeout(function() {
if(c < 10) {
gold = gold + clickPower;
console.log("Clicked");
c++;
setTimeout(arguments.callee, 1000);
//^^ Schedule yourself to be called after 1 second
}
}, 1000);
I did come across this challenge today, and the answer provided by #Guarav Vaish set me on a path to success. In my case, I am using ES6 syntax and its worth noting that the arguments.callee is deprecated. However, here is how I'd write the same solution as contributed by Vaish:
var c = 0;
setTimeout(function named() {
if(c < 10) {
gold = gold + clickPower;
console.log("Clicked");
c++;
setTimeout( ()=>{ named() }, 1000);
//^^ Schedule yourself to be called after 1 second
}
}, 1000);
Note: I am simply using a named function in place of the anonymous one in the original solution. Thank you. This was really helpful

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.

Javascript: how to pass different object to setTimeout handlers created in a loop?

I'm trying to write some JS replicating jQuery's fadeIn and fadeOut functions. Here's the code I have so far:
function fadeIn(elem, d, callback)
{
var duration = d || 1000;
var steps = Math.floor(duration / 50);
setOpacity(elem,0);
elem.style.display = '';
for (var i = 1; i <= steps; i++)
{
console.log(i/steps + ', ' + (i/steps) * duration);
setTimeout('setOpacity("elem", '+(i / steps)+' )', (i/steps) * duration);
}
if (callback)
setTimeout(callback,d);
}
function setOpacity(elem, level)
{
console.log(elem);
return;
elem.style.opacity = level;
elem.style.MozOpacity = level;
elem.style.KhtmlOpacity = level;
elem.style.filter = "alpha(opacity=" + (level * 100) + ");";
}
I'm having troubles with the first setTimeout call - I need to pass the object 'elem' (which is a DOM element) to the function setOpacity. Passing the 'level' variable works just fine... however, I'm getting "elem is not defined" errors. I think that's because by the time any of the setOpacity calls actually run, the initial fadeIn function has finished and so the variable elem no longer exists.
To mitigate this, I tried another approach:
setTimeout(function() { setOpacity(elem, (i / steps));}, (i/steps) * duration);
The trouble now is that when the function is called, (i/steps) is now always 1.05 instead of incrementing from 0 to 1.
How can I pass the object in question to setOpacity while properly stepping up the opacity level?
Your "another approach" is correct, this is how it's usually done.
And as for the problem of i always being a constant, that's how closures work!
You see, when you create this function that does something with i (like function() { alert(i); }), that function, as they say, 'captures', or 'binds' the variable i, so that variable i does not die after the loop is finished, but continues to live on and is still referenced from that function.
To demonstrate this concept, consider the following code:
var i = 5;
var fn = function() { alert(i); };
fn(); // displays "5"
i = 6;
fn(); // displays "6"
When it is written in this way, the concept becomes a bit more evident, doesn't it? Since you're changing the variable in the loop, after the loop is finished the variable retains it's last value of (1+steps) - and that's exactly what your function sees when it starts executing.
To work around this, you have to create another function that will return a function. Yes, I know, kind of mind-blowing, but bear with me. Consider the revised version of my example:
function createFn( theArgument )
{
return function() { alert( theArgument ); };
}
var i = 5;
var fn = createFn( i );
fn(); // displays "5"
i = 6;
fn(); // still displays "5". Voila!
This works, because the fn function no longer binds the variable i. Instead, now it binds another variable - theArgument, which has nothing to do with i, other than they have the same value at the moment of calling createFn. Now you can change your i all you want - theArgument will be invincible.
Applying this to your code, here's how you should modify it:
function createTimeoutHandler( elemArg, iDivStepsArg )
{
return function() { setOpacity( elemArg, iDivStepsArg ); };
}
for (var i = 1; i <= steps; i++)
{
console.log(i/steps + ', ' + (i/steps) * duration);
setTimeout( createTimeoutHandler( elem, i/steps ), (i/steps) * duration);
}
Your first approach is evaluating code at runtime. You are most likely right about why it's failing (elem is not in the scope in which the code is eval'd). Using any form of eval() (and setTimeout(string, ...) is a form of eval()) is a general bad idea in Javascript, it's much better to create a function as in your second approach.
To understand why your second approach is failing you need to understand scopes and specifically closures. When you create that function, it grabs a reference to the i variable from the fadeIn function's scope.
When you later run the function, it uses that reference to refer back to the i from fadeIn's scope. By the time this happens however, the loop is over so you'll forever just get i being whatever it was when that loop ended.
What you should do is re-engineer it so that instead of creating many setTimeouts at once (which is inefficient) you instead tell your setTimeout callback function to set the next Timeout (or you could use setInterval) and do the incrementing if your values inside that callback function.

Categories