I've observed that in the following code:
setTimeout(function(){console.log('setTimeout')});
Promise.resolve(1).then(function(){console.log('promise resolve')})
No matter how many times I execute this, the promise callback always logs before the setTimeout.
My understanding is that both callbacks are scheduled to be executed to the next tick, and I don't really understand what is going on that makes the promise always take precendence over the timeout.
Promise.resolve schedules a microtask and the setTimeout schedules a macrotask. And the microtasks are executed before running the next macrotask.
Short answer Promises have better priority than setTimeout callback function in event loop stack(or how i understand it).
Long answer watch this video. Very helpful. Hope this helps.
https://www.youtube.com/watch?v=8aGhZQkoFbQ
Thanks #MickJuice for new and updated video for event loop.
https://www.youtube.com/watch?v=cCOL7MC4Pl0
setTimeout() has a minimum delay of 4ms, so even though you didn't specify a delay in your code the timeout will still be delayed at least 4ms. Your promise's .then() is called in the meantime.
Timeouts and Promises both serve to execute code in an asynchronous way but with differences characteristics and purposes:
setTimeout
- Delays the execution of a function by specific time duration.
- Does not block the rest of the code execution (asynchronous behavior)
- They create Macrotask (browser internal operation)
Promises
- They are a wrapper to allow asynchronous execution of code(Eg: an ajax call). (Does not depend on specific time duration)
- They are especially useful to chain different async calls.
- Does not block the rest of the code execution (asynchronous behavior) at less you are using the await operator.
- They create Microtask (browser internal operation), which have priority over the Macrotask.
Recommendation
Use setTimeout when you want to delay a function execution some specific time duration and not block the rest of the code execution in the process
Use Promises:
When you want to execute some async code and to avoid the “callback hell” (yes because you can make asynchronous ajax calls without Promises but the syntax is less clear and more prone to errors)
This has to do with the event loop as defined in the Web Spec. The browser has multiple task queues for multiple types of tasks (e.g. timer tasks created through setTimeout), as well as a microtask queue (where Promise settlement gets pushed to). Whenever the browser finishes executing a task, it empties the microtask queue and executes all tasks in it, before continuing with a task from another task queue.
Therefore after the code executes (which is a task), the Promise settlement is inside of the microtask queue, and the timer task might already be inside a task queue¹. The microtask queue gets emptied and the Promise resolves. Then somewhen the timer task runs.
¹ Browsers might choose to increase timeouts a bit, and they do. A timeout will never run after 0ms in most browsers.
Timeouts and Promises serve different purposes.
setTimeout delays the execution of the code block by a specific time duration. Promises are an interface to allow async execution of code.
A promise allows code to continue executing while you wait for another action to complete. Usually this is a network call. So anything in your then() call will be executed once the network call (or whatever the promise is waiting for) is completed. The time difference between the start of the promise and the resolution of the promise entirely depends on what the promise is executing, and can change with every execution.
The reason the promise is executing before your timeout is that the promise isn't actually waiting for anything so it resolved right away.
Related
I've observed that in the following code:
setTimeout(function(){console.log('setTimeout')});
Promise.resolve(1).then(function(){console.log('promise resolve')})
No matter how many times I execute this, the promise callback always logs before the setTimeout.
My understanding is that both callbacks are scheduled to be executed to the next tick, and I don't really understand what is going on that makes the promise always take precendence over the timeout.
Promise.resolve schedules a microtask and the setTimeout schedules a macrotask. And the microtasks are executed before running the next macrotask.
Short answer Promises have better priority than setTimeout callback function in event loop stack(or how i understand it).
Long answer watch this video. Very helpful. Hope this helps.
https://www.youtube.com/watch?v=8aGhZQkoFbQ
Thanks #MickJuice for new and updated video for event loop.
https://www.youtube.com/watch?v=cCOL7MC4Pl0
setTimeout() has a minimum delay of 4ms, so even though you didn't specify a delay in your code the timeout will still be delayed at least 4ms. Your promise's .then() is called in the meantime.
Timeouts and Promises both serve to execute code in an asynchronous way but with differences characteristics and purposes:
setTimeout
- Delays the execution of a function by specific time duration.
- Does not block the rest of the code execution (asynchronous behavior)
- They create Macrotask (browser internal operation)
Promises
- They are a wrapper to allow asynchronous execution of code(Eg: an ajax call). (Does not depend on specific time duration)
- They are especially useful to chain different async calls.
- Does not block the rest of the code execution (asynchronous behavior) at less you are using the await operator.
- They create Microtask (browser internal operation), which have priority over the Macrotask.
Recommendation
Use setTimeout when you want to delay a function execution some specific time duration and not block the rest of the code execution in the process
Use Promises:
When you want to execute some async code and to avoid the “callback hell” (yes because you can make asynchronous ajax calls without Promises but the syntax is less clear and more prone to errors)
This has to do with the event loop as defined in the Web Spec. The browser has multiple task queues for multiple types of tasks (e.g. timer tasks created through setTimeout), as well as a microtask queue (where Promise settlement gets pushed to). Whenever the browser finishes executing a task, it empties the microtask queue and executes all tasks in it, before continuing with a task from another task queue.
Therefore after the code executes (which is a task), the Promise settlement is inside of the microtask queue, and the timer task might already be inside a task queue¹. The microtask queue gets emptied and the Promise resolves. Then somewhen the timer task runs.
¹ Browsers might choose to increase timeouts a bit, and they do. A timeout will never run after 0ms in most browsers.
Timeouts and Promises serve different purposes.
setTimeout delays the execution of the code block by a specific time duration. Promises are an interface to allow async execution of code.
A promise allows code to continue executing while you wait for another action to complete. Usually this is a network call. So anything in your then() call will be executed once the network call (or whatever the promise is waiting for) is completed. The time difference between the start of the promise and the resolution of the promise entirely depends on what the promise is executing, and can change with every execution.
The reason the promise is executing before your timeout is that the promise isn't actually waiting for anything so it resolved right away.
I've been learning about the event loop that browsers use and how it processes macrotasks and microtasks when they are added to their respective queues.
My question is how does the event loop know that a promise has resolved so that it can then run the microtask of the then/catch/finally code?
For example say the promise is an HTTP request. The http request is sent within some function and code execution can continue synchronously. Some time later the promise resolves and the associated then code runs.
Is the HTTP request done on some other browser thread, which when it gets the result it then adds to the microtask queue that the then code can now be executed?
In Javascript, the callback (of the awaited async function) runs only when the call stack is empty. If there are multiple callbacks, then they are queued (in the event loop queue) such that only 1 callback runs in its entirety before the next callback gets change to run.
In case of C# (assuming ConfigureAwait is not set to false - that is - the callabck will run in the same thread; also assuming there is syncronization context [winforms]):
Does the callback run immediately without waiting for call stack to be empty?
Suppose there are multiple callbacks queued, then while 1 callback is running, and another callback is ready for execution, then does the running callback suspend while the new callback wants to run? Or is it sequential?
The answer is that it depends on the current SynchronizationContext and, if that is null, the current TaskScheduler.
When the awaited Task completes, and you have configured the Awaitable to capture the context and recover on it, then almost certainly that continuation cannot continue immediately (the thread is likely busy executing something else). All that can be done is to schedule the continuation for later execution on the current SynchronizationContext, which is done by calling SynchronizationContext.Post().
How this Post method is implemented depends on the SynchronizationContext (which is abstract). If you are using WPF, the SynchronizationContext is a DispatcherSynchronizationContext. There, Post is implemented by a call to Dispatcher.BeginInvoke() with DispatcherPriority.Normal.
In other words, your continuation gets queued on the dispatcher for later execution with normal priority. Other delegates may have higher priority (e.g. DispatcherPriority.Input), but eventually your delegate gets executed on the dispatcher.
Two things to notice:
BeginInvoke simply queues your delegate. It does not execute it right away. In other words, queuing your delegate is really fast.
The above explanation should illustrate why async-await is just syntactic sugar. You could achieve all these calls yourself, but the compiler does it for you, making your code flow more naturally.
I've observed that in the following code:
setTimeout(function(){console.log('setTimeout')});
Promise.resolve(1).then(function(){console.log('promise resolve')})
No matter how many times I execute this, the promise callback always logs before the setTimeout.
My understanding is that both callbacks are scheduled to be executed to the next tick, and I don't really understand what is going on that makes the promise always take precendence over the timeout.
Promise.resolve schedules a microtask and the setTimeout schedules a macrotask. And the microtasks are executed before running the next macrotask.
Short answer Promises have better priority than setTimeout callback function in event loop stack(or how i understand it).
Long answer watch this video. Very helpful. Hope this helps.
https://www.youtube.com/watch?v=8aGhZQkoFbQ
Thanks #MickJuice for new and updated video for event loop.
https://www.youtube.com/watch?v=cCOL7MC4Pl0
setTimeout() has a minimum delay of 4ms, so even though you didn't specify a delay in your code the timeout will still be delayed at least 4ms. Your promise's .then() is called in the meantime.
Timeouts and Promises both serve to execute code in an asynchronous way but with differences characteristics and purposes:
setTimeout
- Delays the execution of a function by specific time duration.
- Does not block the rest of the code execution (asynchronous behavior)
- They create Macrotask (browser internal operation)
Promises
- They are a wrapper to allow asynchronous execution of code(Eg: an ajax call). (Does not depend on specific time duration)
- They are especially useful to chain different async calls.
- Does not block the rest of the code execution (asynchronous behavior) at less you are using the await operator.
- They create Microtask (browser internal operation), which have priority over the Macrotask.
Recommendation
Use setTimeout when you want to delay a function execution some specific time duration and not block the rest of the code execution in the process
Use Promises:
When you want to execute some async code and to avoid the “callback hell” (yes because you can make asynchronous ajax calls without Promises but the syntax is less clear and more prone to errors)
This has to do with the event loop as defined in the Web Spec. The browser has multiple task queues for multiple types of tasks (e.g. timer tasks created through setTimeout), as well as a microtask queue (where Promise settlement gets pushed to). Whenever the browser finishes executing a task, it empties the microtask queue and executes all tasks in it, before continuing with a task from another task queue.
Therefore after the code executes (which is a task), the Promise settlement is inside of the microtask queue, and the timer task might already be inside a task queue¹. The microtask queue gets emptied and the Promise resolves. Then somewhen the timer task runs.
¹ Browsers might choose to increase timeouts a bit, and they do. A timeout will never run after 0ms in most browsers.
Timeouts and Promises serve different purposes.
setTimeout delays the execution of the code block by a specific time duration. Promises are an interface to allow async execution of code.
A promise allows code to continue executing while you wait for another action to complete. Usually this is a network call. So anything in your then() call will be executed once the network call (or whatever the promise is waiting for) is completed. The time difference between the start of the promise and the resolution of the promise entirely depends on what the promise is executing, and can change with every execution.
The reason the promise is executing before your timeout is that the promise isn't actually waiting for anything so it resolved right away.
I am a bit confused about when the event loop spins in the browser.
The questions are:
Does a task and the pending microtasks, happen in the same loop iteration/turn/tick?
Which are the actual conditions that need to be met in order for the loop to turn?
Are these conditions the same in node.js event loop? - I don't know if this is a stupid question.
Let's say we have a webpage and in the front end we have JavaScript code that schedules a task and waits for a promise (which is a microtask). Is the execution of the promise considered to happen in the same turn of the event loop as the task, or in different iterations?
I currently assume that they all happen in the same iteration. Since if betting otherwise, in the case of microtasks executing while mid-task, that would mean that the task would require multiple loop iterations in order to fully complete. Which seems wired to me. Would it be correct to also say that the update rendering part, that may occur after each task, happens in the same loop turn?
Thank you in advance!
------------------------------------------------------------------------------------
I know I am supposed to add a comment, but it is going to be a long one, and I also need to write code, so I am editing the question and asking for clarification here.
#T.J. Crowder Thank you so much for your time and detailed explanation!
I had indeed misread "microtasks are processed after callbacks (as long as no other JavaScript is mid-execution)" in this great article and had gotten a bit confused.
I also had questions about the 4ms setTimout for which I couldn't find information about, so thanks for that info also.
One last thing, though... If we were to mark the loop ticks between the example code, where would we put them (assuming console.logs do not exist)?
Suppose we have a function named exampleCode, having the following body:
setTimeout(setTimeoutCallback, 0);
Promise.resolve().then(promiseCallback);
For the above code, my guess would be...
Just before executing exampleCode (macro)task:
first loop tick
setTimeoutCallback (macro)task scheduling
Promise.then microtask scheduling
promiseCallback execution
second loop tick
setTimeoutCallback execution
third loop tick
Or is there an additional loop tick between between Promise.then microtask scheduling and promiseCallback execution ?
Thank you in advance once again!
Does a task and the pending microtasks, happen in the same loop iteration/turn/tick?
The task occurs, then when it ends, any pending microtasks it scheduled are run.
Which are the actual conditions that need to be met in order for the loop to turn?
It's not clear what you mean by this. It may be easier to think in terms of jobs and a job queue (which is the ECMAScript spec's terminology): If there is a pending job and the thread servicing that queue is not doing something else, it picks up the job and runs it to completion.
Are these conditions the same in node.js event loop?
Close enough, yes.
Let's say we have a webpage and in the front end we have JavaScript code that schedules a task and waits for a promise (which is a microtask). Is the execution of the promise considered to happen in the same turn of the event loop as the task, or in different iterations?
In a browser (and in Node), it happens after the task completes, when the task's microtasks (if any) are run, before the next queued task/job gets picked up.
For instance:
// This code is run in a task/job
console.log("Scheduling (macro)task/job");
setTimeout(() => {
console.log("timeout callback ran");
}, 0);
console.log("Scheduling microtask/job");
Promise.resolve().then(() => {
console.log("promise then callback ran");
});
console.log("main task complete");
On a compliant browser (and Node), that will output:
Scheduling (macro)task/job
Scheduling microtask/job
main task complete
promise then callback ran
timeout callback ran
...because the microtask ran when the main task completed, before the next macrotask ran.
(Note that setTimeout(..., 0) will indeed schedule the timer to run immediately on compliant browsers provided it's not a nested timeout; more here. You'll see people saying there is no "setTimeout 0", but that's outdated information. It's only clamped to 4ms if the timer's nesting level is > 5.)
More to explore:
MDN Concurrency Model and Event Loop
ECMAScript Spec: Jobs and Job Queues
WHAT-WG "HTML5" Spec: Event Loops, Processing Model, Timers, and Timer Initialization Steps
Re the code and question in the edit/comment:
setTimeout(setTimeoutCallback, 0);
Promise.resolve().then(promiseCallback);
Your guess looks pretty good. Here's how I'd describe it:
Schedule the task to run that script
(When thread is next free)
Pick up the next task (from #1 above)
Run that task:
Create the task for the timer callback
In parallel, queue the task when the time comes
Queue a microtask for the promise then callback
End of task
Microtask check
Run the then callback
(When thread is next free)
Pick up the next task (the one for the timer callback)
Run the task
End of task
Microtask check (none to do in this case)
The processing model text explicitly calls out that the task ends prior to the microtask check, but I don't think that's observable in any real way.