Why the function called by setTimeout has no callstack limit?

setTimeout is asynchronous (it returns before executing the callback), and the callback will be executed on a new, empty stack frame. That's the whole purpose. It will never overflow the stack.

It is not a recursive call, for which scope (in case of a non-tail-call-optimized function) would need to be retained. But that would also mean that the function became blocking, which is not what you want.


This is because the Timeout callbacks are not stored in the stack as you are assuming: there are in a queue, waiting to be executed in its own stack. And in your code the queue is filled when the previous execution is completed, so the queue is not growing.

UPDATE: You can check the specs here, but I'm copying the text:

The setTimeout() method must run the following steps:

  1. Let handle be a user-agent-defined integer that is greater than zero that will identify the timeout to be set by this call.

  2. Add an entry to the list of active timeouts for handle.

  3. Get the timed task handle in the list of active timeouts, and let task be the result.

  4. Get the timeout, and let timeout be the result.

  5. If the currently running task is a task that was created by the setTimeout() method, and timeout is less than 4, then increase timeout to 4.

  6. Return handle, and then continue running this algorithm asynchronously.

  7. If the method context is a Window object, wait until the Document associated with the method context has been fully active for a further timeout milliseconds (not necessarily consecutively).

  8. Otherwise, if the method context is a WorkerUtils object, wait until timeout milliseconds have passed with the worker not suspended (not necessarily consecutively).

  9. Otherwise, act as described in the specification that defines that the WindowTimers interface is implemented by some other object.

  10. Wait until any invocations of this algorithm started before this one whose timeout is equal to or less than this one's have completed.

    Optionally, wait a further user-agent defined length of time.