I have the following code, which does not work as I expect.
const bar = () => console.log('bar')
const baz = () => console.log('baz')
const myPromise = new Promise((resolve, reject) =>
resolve('should be right after baz, before bar')
);
const foo = () => {
console.log('foo')
setTimeout(bar, 0)
myPromise.then(resolve => console.log(resolve))
baz()
}
foo()
console.log('before bar')
The result:
foo
baz
before bar
should be right after baz, before bar
bar
According to what's written in https://nodejs.dev/ , promise resolve will happen right after the function.
Promises that resolve before the current function ends will be executed right after the current function.
So I expect 'should be right after baz, before bar' to happen right after the end of foo()
, before console.log('before bar')
.
Can someone explain why it doesn't?
CodePudding user response:
Callbacks passed to setTimeout
are added in the task queue whereas the callbacks related to promise fulfilment or rejection are added in the micro-task queue.
These queues are only processed after the synchronous execution of your script, i.e. the javascript code you wrote.
As a result, 'before bar'
is logged before any asynchronous callbacks are invoked.
How your code executes?
Script execution starts.
Function
bar
,baz
, andfoo
are defined. Promise constructor is also called which leads to the invocation ofresolve
function, resolving the promise synchronouslyfoo
function is called'foo'
is logged on the consolesetTimeout
is called, schedulingbar
to be called asynchronously.bar
will be added in the task queuethen
method is called onmyPromise
, scheduling the fulfilment handler to be invoked asynchronously. This fulfilment handler is added in the micro-task queuebaz
function is called, logging'baz'
on the console
foo
function ends. Control return to the next line from wherefoo
was calledLast
console.log
statement is executed, logging'before bar'
on the consoleScript execution ends
At this point, console output is as shown below:
foo
baz
before bar
and the task queue contains a task to execute the bar
function whereas the micro-task queue contans a micro-task to execute the fulfilment handler of the myPromise
.
After the script execution ends, task and micro-task queues can be processed.
The micro-task queue is processed:
- after each callback as long as the call-stack is empty (this is what nodejs.dev is referring to)
- after each task
Script execution is a task and after its execution, micro-task queue will be the first one to be processed, logging 'should be right after baz, before bar'
on the console.
At last, the task queue is processed, baz
function is called, logging 'baz'
on the console.
Following might be a helpful read to understand the micro-task and the task queue: JavaScript async callbacks - Promise and setTimeout
CodePudding user response:
The MDN documentation for Promise.prototype.then
states (emphasis is mine):
Once a Promise is fulfilled or rejected, the respective handler function (onFulfilled or onRejected) will be called asynchronously (scheduled in the current thread loop).
You might find the snippet below enlightening:
Promise.resolve("I resolved").then((v) => console.log(v));
console.log("hello");
CodePudding user response:
If want to understand the asynchronous concept, you should check out this awesome video.
I'll try to explain to you the sequence of how the code is running here
First of all, we are Initializing all the variables with functions.
Then we're calling foo()
, and later console.log('before baz')
.
What will happen on calling foo
is foo
will enter the stack. And foo
is logged into the console.
Then setTimeout(bar, 0)
is being called. setTimeout(bar, 0)
being asynchronous, will be called later and pushed in the callback queue(this will be called later when the stack is empty). That's why we didn't see the console of bar
.
After this myPromise.then(resolve => console.log(resolve))
is called. And this is also an asynchronous call. So this will also be pushed to the callback queue.
Then baz()
will be executed. And that's why we're seeing a log of baz
after the foo
.
Now remember, we've console.log('before bar')
in the stack, so this will be logged first. And after this all the items in the callback queue will be called.
Three things are logged up to now:-
foo
baz
before bar
Now, the good thing about promises is they are being prioritized in the callback queue, over anything. So even setTimeout
is being called before the our promise, if promise is executed, it will be prioritized.
That's the reason why 'should be right after baz, before bar'
is called before bar
.