once a job starts to execute, even if it is an I/O bound, it will still finish it up first
You have a slight misunderstanding of what non-blocking is. This description is true regardless if the architecture is blocking or non-blocking. What non-blocking actually means is that waiting for events use interrupts instead of a polling loop.
This is a strong indicator of your misunderstanding:
After A and B are finished, C would be pushed onto the call stack and starts to execute
This is 99.99% false. C will not be pushed onto the call stack after A and B are finished. This strongly implies that you misunderstand what waiting for I/O actually do.
Now, how the runtime actually process A, B and C depends on how you initially call them.
For example, assuming that there is a function Z
that is asynchronous that is configured to execute C
. If you did:
A();
B();
Z().then(C);
Then A will be executed first. After that B will be executed, after that Z will put C onto the event queue (it's called a queue but it is really just a list of event handlers).
When the event that Z waits for happens the runtime will call C. This is exactly like a regular function call (as if you called C()
) but instead of being called by your code it is called by the code that processes the event.
On the other hand if you did:
Z().then(C);
A();
B();
Z will be executed first and it will add C to the event queue. After adding C to the event queue A is executed and then B.
When the event that Z waits for happens the runtime will call C. If by coincidence the event that Z waits for happens during the execution of A the runtime will not even notice and will complete executing A and B. At the end of the execution of A and B the runtime will check the event loop and will immediately find that it needs to call C.
So in this one unlikely case, then yes - C will be executed after A and B completes. But that is only a coincidence. The reason C is executed is not because A and B completes but because the event that it is waiting for has happened.
For more details on how all this works you may want to look at some of my old answers to other questions:
I know that callback function runs asynchronously, but why?
How does node.js schedule asynchronous and synchronous tasks?