Reputation: 293
I'm practicing using recursion, and there's something I don't quite get. For example, I wrote this simple countdown function, which is supposed to wait until a second elapsed until counting down to the next second.
I first wrote it like so:
function countdown(sec) {
console.warn(sec);
if(sec > 0) {
sec--;
setTimeout(countdown(sec), 1000);
}
}
It does not wait one second between each log. This works:
function countdown(sec){
console.warn(sec);
setTimeout(function() {
sec--;
if (sec > 0) {
countdown(sec);
}
}, 1000);
};
I don't really understand what's wrong with the first approach. I guess it's something with setTimeout that I don't quite understand, and scoping..?
Thanks in advance for any explanations.
--- edited & working, thanks guys! ---
I didn't know about bind being used as a shorthand.
function countdown(sec) {
console.warn(sec);
if (sec > 0) {
sec--;
setTimeout(countdown.bind(null, sec), 1000);
}
}
Upvotes: 0
Views: 92
Reputation: 135217
JosephNields is correct for why your code was not working, but I'd also like to stress that recursion generally doesn't involve mutating state values, ie sec--
Instead, just pass sec - 1
as the next value for countdown
. In other words, there's no gain in setting sec
to a smaller number, just recurse with the smaller number
var countdown = function (sec) {
console.log(sec)
if (sec > 0)
setTimeout(countdown, 1000, sec - 1)
}
countdown(10)
Also, wouldn't it be great to know when a timer is done? This example shows passing around another value as you recurse.
var countdown = function (sec, done) {
console.log(sec)
if (sec > 0)
setTimeout(countdown, 1000, sec - 1, done)
else
done()
}
countdown(5, function () {
console.log('timer is all done!')
})
Upvotes: 1
Reputation: 5661
the first argument to setTimeout
should be a function.
When you call setTimeout(countdown(sec),1000)
, it gets evaluated to setTimeout(undefined, 1000)
because countdown(sec)
does not return a value.
Your second method works, but as shorthand you could also do this: setTimout(coundown.bind(null, sec), 1000)
. This creates a function that will execute countdown(sec)
when called, which is basically equivalent to the following:
setTimeout(
function() {
countdown(sec);
},
1000
);
More info here: Use of the JavaScript 'bind' method
Upvotes: 0