0

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); } } 
5
  • I think you are getting confused with passing a function as argument vs calling the function. The first one calls a function hence the recursion and the second one passes a function as parameter which is what is required Commented Mar 15, 2017 at 19:45
  • Google for "settimeout called immediately", there are too many results there and I cannot pick one that is the best. Commented Mar 15, 2017 at 19:45
  • Possible duplicate of Passing a function as an argument in a javascript function Commented Mar 15, 2017 at 19:46
  • setTimeout expects first parameter as a function, In your first approach - you are executing the function. so you can modify you setTimeout call to setTimeout(countdown.bind(this, sec), 1000);. Here bind will return a closure i.e) a scoped function which holds your sec value Commented Mar 15, 2017 at 19:48
  • oh thank you ajai & smac89! understood. yes indeed, in the first approach, I'm calling countdown(sec) so, this works, even without the bind: setTimeout(function(){countdown(sec)}, 1000) Thank you! Commented Mar 15, 2017 at 19:53

2 Answers 2

1

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!') })

Sign up to request clarification or add additional context in comments.

3 Comments

to be fair sec - 1 doesn't really mutate state. Setting sec in one closure won't change its value in another. But it is cleaner to just write sec-1
I'm not sure what you're saying. sec - 1 does not mutate state; that is correct. But! "setting sec in one closure" – assuming you're referring to sec--does actually mutate state. It's only irrelevant in this case because Numbers are pass-by-value in a function application.
that's what I mean - it's just passing the value. So if you access sec somewhere after the recursive call, it will have the value originally passed to the recursive call, and not any value set by the recursive call
0

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.