Surprisingly, no! I believe your code should run fine.
Asynchronous operations in JavaScript, such as setTimeout()
, XMLHttpRequest
, and Promise
utilize something called the Event Loop to execute outside of the normal flow of the program. Effectively, when something asynchronous is going to happen, it gets added to the 'queue' (which is entirely separate from the stack). Once the stack has emptied, the Event Loop will start processing these queued messages, executing their associated functions one by one.
Pretty much everything asynchronous in JavaScript works this way, and that includes the Q promises library. So in the case of your example, this is a (very) simplified explanation of what's happening:
main()
is called, creating a new stack frame.
Q.fcall(somePromiseFunction)
is called, creating a second stack frame (and presumably a third, when somePromiseFunction
is called. This sets off the asynchronous operation in the background, with the function passed to then
set as the callback.
Q.fcall(somePromiseFunction)
has now returned, so those stack frames are cleared.
main()
has also reached the end, so that gets cleared too - we're back to an empty stack.
- Once the asynchronous operation is complete, a message gets pushed onto the event queue, with your
then
callback associated to it.
- The stack is empty, so the event loop starts processing.
- Your callback gets executed.
The important thing to notice here is that your code does not recurse! Your then
callback only ever gets called from the event queue, not by main()
, so the stack is able to clear without problems between iterations.
I would recommend watching the talk 'What the heck is the event loop anyway?' for more information on how this all works - the way they explained it was what made it finally click for me.
EDIT:
In response to your questions in the comments, I'll clarify a few things. A recursive function, in its most simple form, looks like this:
function recursive() {
recursive();
}
Calling a function allocates a stack frame, which is cleared when it returns. However, without some sort of way of breaking out of the loop, a recursive function such as this one is never going to return - it will just keep going, endlessly allocating stack frames until you get a stack overflow error. Not that this means you should never use recursion in JavaScript - it can be quite useful sometimes! But you need to be aware that if it recurses too much without returning, you're going to hit the top of the stack.
In answer to your other question - the event loop/your callback doesn't wait for main()
in particular to end, it just waits for the stack to clear. Here's a simplified visualization of what the stack does in your program:
[]
[main]
[main, fcall]
[main, fcall, somePromiseFunction]
[main, fcall]
[main]
[main, then]
[main]
[] // Main returned, so the stack is clear
[yourCallback] // The event loop kicks in, runs your callback
[yourCallback, main]
... // Above steps repeat
[yourCallback] // Main returned
[] // Stack clears, so the event loop kicks in again
Compare this to my endlessly recurring function:
[]
[recursive]
[recursive, recursive] // Nothing stops the recursion,
[recursive, recursive, recursive] // so the stack is never going to clear!