Your call stack will look like this as your code goes through it's loop:
main() [local foo]
main() -> foo [local i = 0]
main()
(empty)
setTimeout()
setTimeout() -> foo [local i = 1]
setTimeout()
(empty)
setTimeout()
setTimeout() -> foo [local i = 2]
setTimeout()
(empty)
over and over until i equals 1000.
It happens this way because setTimeout is a browser api that waits n amount of time and then inserts the callback into the callback queue. The callback will then get picked up by the event loop and executed when the callstack is empty.
So, no, you aren't in any danger of overloading the browser with stacks or vars or whatever you were worried about. Your callstack will remain small because each time foo is executed, it fires off a setTimeout and returns. the i will stay in memory until the setTimeout's callback gets executed, at which point a new i will be created within the scope created by executing foo. That new i will stay in memory until the next setTimeout executes, on and on.
Here's a video that may help explain how this works. http://www.youtube.com/watch?v=8aGhZQkoFbQ