I was testing out concepts of asynchronous code in JS . Got confused between callback queue & microtask queue order. Whenever promise objects gets resolved , the fulfillment method { then } is pushed into microtask queue while the callbacks of browser timer functions such as setTimeout is pushed into callback queue. Event loop continuously checks queue and pushes functions from queue into call stack whenever call stack gets empty. Event loop should prefer microtask queue over normal callback queue but in the example : https://jsfiddle.net/BHUPENDRA1011/2n89ftmp/ it's happening otherwise.
function display(data) {
console.log('hi back from fetch');
}
function printHello() {
console.log('hello');
}
function blockfor300ms() {
for (let i = 0; i < 300; i++) {
// just delaying logic
}
}
// this sets the printHello function in callback queue { event loop queue }
setTimeout(printHello, 0);
const futureData = fetch('https://api.github.com/users/xiaotian/repos');
// after promise is resolved display function gets added into other queue : Microtask queue { job queue}
futureData.then(display);
// event loop gives prefrence to Microtask queue ( untill its complete)
blockfor300ms();
// which runs first
console.log('Me first !')
expected output
Me first !
hi back from fetch
hello
actual output :
Me first !
hello
hi back from fetch
Kindly let me know how it's happening over here.
Thanks
While it is true, what "kib" stated:
"your function blockfor300ms doesn't block the thread long enough for
the fetch to receive a response"
sadly this is irrelevant, because even if you did block execution until after you received a response to your fetch call, you would still see the same result...
(see sample code snippet below, you can block execution with an alert box or a long loop of non-async XMLHttpRequest calls, I received the same result)
Unfortunately, fetch does not work as described by all the blogs and resources I've found... which state that
Fetch will add it's promise chain to the micro-tasks queue and run before any callbacks on the next tick of the event loop.
From the sample code below, it appears fetch does not simply add it's resolve handler function to the micro-tasks queue as described by others, because as Lewis stated since it requires network activity it is being handled by the Network Task Source. But, I don't believe this is due to printHello "blocking" the network task, because fetch is being fired prior to printHello in my sample code below, and the network response would occur before the timer completed as well.
As you can see in the example below I have the printHello delayed to be added to the Task Queue long after the fetch response has been received (2000ms), yet if we block the code execution for longer than 2000ms (so that there is still running execution context) then "Hello" will be printed first. Meaning the fetch resolve() handler is NOT being simply added to the Micro-Task Queue where it would have fired along with the other promise handlers.
So, why is it still being logged after the callback if the response is received and theoretically added to the Task Queue prior to the timer task completing (at 2000ms)? Well, my guess is that the timer task source must be receiving priority over that of the network task source. And therefore both are sitting in their task queue, but the timer task queue is firing before the network task queue...
Links to specs:
Timer Task Source - https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timer-task-source
Networking Task Source - https://html.spec.whatwg.org/multipage/webappapis.html#networking-task-source
function display(data){console.log("Fetch resolved!")}
function printHello(){console.log("Callback Time")}
function blockExecution() {
console.log("Blocking execution...");
alert('Wait at least 2000ms before closing');
}
const futureData = fetch('https://jsonplaceholder.typicode.com/todos/1');
futureData.then(response => response.json()).then(display);
setTimeout(printHello, 2000);
const p = new Promise(
// this is called the "executor"
(resolve, reject) => {
console.log("I'm making a promise...");
resolve("Promise resolved!");
console.log("Promise is made...");
}
);
p.then(
// this is called the success handler
result => console.log(result)
);
blockExecution();
console.log("Execution ended!");
futureData is actually a fetch promise so there is absolutely a network task queued into task queue when fetch is called. As the result, printHello will definitely be executed before the network task since they're both tasks. And method display will only be put into microtask queue when the promise of network task is resolved. Microtasks, by definition, are only executed at the end of each task. So display will be called at the end of the network task when printHello has already been called long time before.
If you want display to be called before printHello, futureData must only queue microtasks. Let's modify your example a bit.
function display(data) {
console.log('hi back from fetch');
}
function printHello() {
console.log('hello');
}
let now = Date.now();
function executeFutureDataWithMicrotasksOnly() {
// Execute microtasks continually in 300ms.
return Promise.resolve().then(() => Date.now() - now < 300 && executeFutureDataWithMicrotasksOnly());
}
function blockfor300ms() {
for (let i = 0; i < 300; i++) {
// just delaying logic
}
}
// this sets the printHello function in callback queue { event loop queue }
setTimeout(printHello, 0);
const futureData = executeFutureDataWithMicrotasksOnly();
// after promise is resolved display function gets added into other queue : Microtask queue { job queue}
futureData.then(display);
// event loop gives prefrence to Microtask queue ( untill its complete)
blockfor300ms();
// which runs first
console.log('Me first !')
As you can see from the above example, if you replace fetch with a method having only microtasks, the execution order is changed as expected though both fetch and executeFutureDataWithMicrotasksOnly are executed in a similar time interval. When futureData no longer queues tasks, all microtasks including display will be executed at the end of the currently executing task, which is the previous task of task printHello.
I think the problem comes from the fact that your function blockfor300ms doesn't block the thread long enough for the fetch to receive a response.
There won't be anything in the job queue (yet) when the event loop will see that it can call printHello.
Using Visualization
This will help you in giving a better understanding of how javascript works.
Here in this case fetch(Promise) is taking more time than setTimeout so when the event loop cycle is running fetch(Promise) was still in progress and setTimeout is executed first as it took less time and came out of the task queue and it gets processed and when fetch(Promise) ends it comes out of the microtask queue.
If you will increase setTimeout time then the first fetch(Promise) will occur first and then setTimeout will occur. Hope this solves your question.
It seems to be easier than we all think:
It is possible for a microtask to be moved to a regular task queue, if, during its initial execution, it spins the event loop. HTML Living Standard #queue a microtask
When the loop is in the process of selecting a task form the task queue, it can choose to execute tasks that were previously queued into the microtask queue and now are part of task queue:
In that case, the task chosen in the next step was originally a microtask, but it got moved as part of spinning the event loop. HTML Living Standard #event loop processing model
Code that spins the loop is anything that includes parallel operations:
In parallel:
Wait until the condition goal is met.
HTML Living Standard #spin-the-event-loop
Related
Using native Javascript Promise:
Promise.resolve(1).then(it => console.log(it))
console.log(2)
This is logged:
2
1
Question: how is it possible for 2 to execute before 1? JS being event-driven, what is the event that is executing the callback given to then when the original caller has already left that execution tree? Is the engine doing some kind of behind-the-scene magic here?
JavaScript maintains something called a callstack. This is used to keep track of whereabouts in the script we are. When you call a function, it gets added to the callstack, and when the function returns/finishes, it gets removed/popped off the callstack. It is also helpful to think of your entire script also as being in its own "function", and so, when your script first begins its executing, we add "script" to the callstack:
Stack:
- Script
When your Promise resolves, it executes its associated .then() method and adds the callback to something called the micro-task queue. This queue (along with the macro-task queue) is used so that JavaScript can manage asynchronous events. As a result, once you run:
Promise.resolve(1).then(it => console.log(it))
the state of your queues/stacks looks like so (note, this is the state after Promise.resolve(1) and .then() have been added/popped off the callstack):
Stack:
- Script
Micro task queue:
- it => console.log(it)
The callback in the micro-task queue will only execute once it gets added to the Stack. This happens through the use of the event-loop. The event-loop will pull tasks off the micro-task queue only when the callstack is empty. Currently, we are still running "script", so the callstack isn't empty yet.
Next, you encounter your console.log(2), as this is a function call, it gets added to the call stack, and once it returns it gets popped off. This is the reason why you see 2 in the console appear first, as the Promise's .then() callback hasn't executed yet, as it's sitting in the micro-task queue waiting for the main script to finish. One the main script finishes, "script" gets popped off the stack:
Stack:
- (empty) <----------------<
| --- gets moved to the stack by the event-loop
Micro task queue: |
- it => console.log(it) ---^
the event loop then moves the task(s) from the micro-task queue now that the callstack is empty. Once the task is moved to the callstack, it executes, running the second console.log(it). This is why you see 1 logged in the console after the 2.
I was looking into async behaviour in JS and it was going well for the most part. I understand the synchronous way of executing code, the single thread of JS and how callbacks such as the one inside setTimeout will be timed by the Web browser API, and later on added to the task queue.
The event loop will constantly check the call stack, and only when it is empty (all sync code has executed), it will take functions that have been queued in the task queue. Pushes them back to the call stack and they are executed.
This is pretty straight forward and is the reason why following code:
console.log('start');
setTimeout(() => console.log('timeout'), 0);
console.log('end');
Will output start, end, timeout.
Now when I started reading about promises, I understood that they have higher priority than regular async code such as timeout, interval, eventlistener and instead will get placed in the job queue/microtask queue. The event loop will first prioritize that queue and run all jobs until exhaustion, before moving on to the task queue.
This still makes sense and can be seen by running:
console.log('start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
console.log('end');
This outputs start, end, promise, timeout. Synchronous code executes, the then callback gets pushed to the stack from the microtask queue and executed, setTimeout callback task from the task queue gets pushed and executed. All good so far.
I can wrap my head around the example above where the promise gets resolved immediately and synchronously, as told by the official documentation. The same would happen if we were to create a promise with the new keyword and provide an executor function. That executor function will execute synchronously and resolve the function. So when then is encountered, it can just run asynchronously on the resolved promise.
console.log('start');
const p1 = new Promise(resolve => {
console.log('promise 1 log');
resolve('promise 1');
});
p1.then(msg => console.log(msg));
console.log('end');
The snippet above will output start, promise 1 log, end, promise 1 proving that the executor runs synchronously.
And this is where i get confused with promises, let's say we have the following code:
console.log('start');
const p1 = new Promise(resolve => {
console.log('promise 1 log');
setTimeout(() => {
resolve('promise 1');
}, 0);
});
p1.then(msg => console.log(msg));
console.log('end');
This will result in start, promise 1 log, end, promise 1. If the executor function gets executed right away, that means that the setTimeout within it will get put on the task queue for later execution. To my understanding, this means the promise is still pending right now. We get to the then method and the callback within it. This will be put in the job queue. the rest of the synchronous code is executed and we now have the empty call stack.
To my understanding, the promise callback will have the priority now but how can it execute with the still unresolved promised? The promise should only resolve after the setTimeout within it is executed, which still lies inside the task queue. I have heard, without any extra clarification that then will only run if the promise is resolved, and from my output i can see that's true, but i do not understand how that would work in this case. The only thing i can think of is an exception or something similar, and a task queue task getting the priority before the microtask.
This ended up being long so i appreciate anyone taking the time to read and answer this. I would love to understand the task queue, job queue and event loop better so do not hesitate posting a detailed answer! Thank you in advance.
We get to the then method and the callback within it. This will be put in the job queue.
No, calling then doesn't put anything in the job queue immediately if the promise is still pending. The callback will be installed on the promise for execution later when the promise is fulfilled, just like an event handler. Only when you call resolve(), it actually puts it in the job queue.
This works just like the setTimeout, where you wrote "[the] callback […] will be timed by the Web browser API, and later on added to the task queue" - it doesn't immediately queue a task that somehow waits, but it waits and then queues a task to execute the callback.
... the promise callback will have the priority now ...
Tasks in the microtask queue are given priority over those in the task queue only when they exist.
In the example :
No microtask is queued until after the setTimout() task has resolved the Promise.
The task and microtask are not in competition. They are sequential.
Delays imposed by the task queue and microtask queue (in that order) are additive.
... but how can it execute with the still unresolved promised?
It doesn't. The .then() callback will execute only after the promise is fulfilled, and that fulfillment is dependent on a task placed in the task queue by setTimeout() (even with a delay of zero).
JS engine has got 1 call stack, macro task queue, micro task queue and web api's. More about basic concept: https://stackoverflow.com/a/30910084/1779091
In case of Promise, code inside the promise will run and when resolve is called, then the callback gets added into the micro queue.
Whereas setTimeout runs in the web api and once it completes, it puts the callback into the macro queue.
console.log('start');
setTimeout(() => console.log('timeout'), 0);
console.log('end');
Print start
Call the setTimeout web api and pass the call back into it
At this point the setTimeout may or may not have already completed. Whenever timer is exhausted, the callback will be put into the macro queue
Print end
Nothing left to execute, so check if the queue has something. This will output the timeout.
console.log('start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
console.log('end');
Print start
Call the setTimeout web api and pass the call back into it
At this point the setTimeout may or may not have already completed. Whenever timer is exhausted, the callback will be put into the macro queue
Promise is resolved which puts the callback (the thing after .then) into the micro task queue
Print end
Nothing left to execute, so check if the queue has something. Micro task queue has higher priority then macro task queue. So 1st it will take the callback from micro task into the call stack and print promise and then take the callback from the macro task queue into the call stack and print timeout.
console.log('start');
const p1 = new Promise(resolve => {
console.log('promise 1 log');
resolve('promise 1');
});
p1.then(msg => console.log(msg));
console.log('end');
Print start
Create the promise and assign it to p1
Run the p1 which prints promise 1 log, then resolve which puts the callback (the thing after .then) into the micro task queue
Print end
Nothing left to execute, so check if the queue has something. The callback from the Micro task is put into the stack and it will print promise 1
console.log('start');
const p1 = new Promise(resolve => {
console.log('promise 1 log');
setTimeout(() => {
resolve('promise 1');
}, 0);
});
p1.then(msg => console.log(msg));
console.log('end');
Print start
Create the promise and assign it to p1
Run the p1 which prints promise 1 log, then calls setTimeout which invokes the web api. At this point the setTimeout may or may not have already completed. Whenever timer is exhausted, the callback will be put into the macro queue
Print end
Nothing left to execute, so check if the queue has something. The callback from the Macro task is put into the stack and it will run the resolve which puts the callback (the thing after .then) into the Micro task queue.
Nothing left to execute, so check if the queue has something. The callback from the Micro task is put into the stack and it will print promise 1
I was asked during the interview
"Is it possible to have a piece of code to be scheduled to execute immediately - i.e. jumping the queue - without putting it at the end of event loop's queue".
Initially I thought that this is something done by requestIdleCallback (as I recently read about React Fiber), but after checking requestIdleCallback docs I'm not sure if this is how it actually works i.e. when current callstack gets empty and there was requestIdleCallback called, then ignore what is in engine's event queue and run the callback.
So... Is it possible or not?
Yes it's possible, this is even exactly what the queue a microtask task is supposed to do: push a new microtask at the end of the current job queue, i.e before the end of the current event loop.
This can be achieved by the quite new Window.queueMicrotask() method (which is currently only supported by Webkit & Blink if I'm not mistaken).
if(!window.queueMicrotask)
console.error("your browser doesn't support the queueMicrotask method");
else {
// idle
if(window.requestIdleCallback)
requestIdleCallback(() => console.log('five idle'));
// timeout
setTimeout(() => console.log('four timeout'), 0);
// message event (beginning of next event loop)
onmessage = e => console.log('three message');
postMessage('', '*');
// "queue a microtask"
queueMicrotask(() => console.log('two microtask'));
// synchronous
console.log('one sync');
}
But even in browsers that don't support this method, we have since quite long time access to other methods that will "queue a microtask".
For one, Promise.resolve's callback will get queued as a microtask, so we could also just do
Promise.resolve()
.then(functionToBeCalledBeforeEventLoopsEnd);
// idle
if(window.requestIdleCallback)
requestIdleCallback(() => console.log('six idle'));
// timeout
setTimeout(() => console.log('five timeout'), 0);
// message event (beginning of next event loop)
onmessage = e => console.log('four message');
postMessage('', '*');
// Promise microtask
Promise.resolve().then(() => console.log('two Promise.resolve microtask'));
// obvious microtask (third because Promise's as been queued before)
window.queueMicrotask && queueMicrotask(() => console.log('three microtask'));
// synchronous code
console.log('one sync');
But even before Promises, it was already possible to queue a microtask, by using the MutationObserver API, since mutation records have to be queued as microtasks.
// idle
if(window.requestIdleCallback)
requestIdleCallback(() => console.log('six idle'));
// timeout
setTimeout(() => console.log('five timeout'), 0);
// message event (beginning of next event loop)
onmessage = e => console.log('four message');
postMessage('', '*');
// Mutation microtask
const obs = new MutationObserver(() => console.log('two mutation-microtask'));
obs.observe(test, { attributes: true });
test.className = 'foo';
// obvious microtask (third because Mutation's has been queued before)
window.queueMicrotask && queueMicrotask(() => console.log('three microtask'));
// synchronous code
console.log('one sync');
<div id="test"></div>
But beware, this also means that you can create infinite loops even if these methods are asynchronous, because the event loop would never reach its end.
const asynchronousBlockingLoop = () =>
queueMicrotask(asynchronousBlockingLoop);
And for what requestIdleCallback does, it is waiting for when the browser has nothing to do anymore, and this can be in quite a few event loops.
I'd rather say "No", because of the way the JS runtime works and you cannot alter that from within JS code.
The runtime processes all events and tasks in a queue sorted by age (oldest actions/tasks/etc. are processed first) and the runtime "runs-to-completion" (meaning it processes each task from start to end before running the next task).
For example, if you write setTimeout( <FUNCTION> , 0), the function won't be executed immediately – instead, the browser pushes a message to the queue and processes it as fast as possible. Even if there isn't anything else the runtime does at that point, the function is executed with a delay of a few milliseconds (note that the number passed in isn't guaranteed to be the exact time when your function runs – it instead resembles a guaranteed amount of time which passes by before the function runs). The same goes for setImmediate (where supported).
MDN
Update
The above isn't exactly true for Nodejs. In Node, using process.nextTick, you can place an action at the front of the queue. However, it won't be processed immediatly if the event loop already processes another task. Therefor, the correct answer should be "Yes, but only if your code runs on Nodejs, it is possible to place a task at the beginning of the queue. But it still isn't guaranteed to run immediatly".
setInterval(function(){console.log("hello")},2000);
while(true){}
"hello" never gets printed.
I think event loop runs in a different thread, but here it seems like 'while loop' is preventing the 'event loop' from execution.
Can somebody put some light on this?
You have to understand
How browser internally does setTimeout?
I will explain in brief.
To learn more about this
Here is more detail explanation of event loop by Philip Roberts
in jsconf2014
Philip Roberts: What the heck is the event loop anyway?
Also, to see this process in action there is a great tool take a look
at loupe
To understand that you have to know about event queue in javascript. There are event queues implemented in browser. Whenever an event get triggered in js, all of these events (like click etc.. ) are added to this queue. When your browser has nothing to execute it takes an event from queue and executes them one by one.
Now, when you call setTimeout or setInterval your callback get registered to an timer in browser and it gets added to the event queue after the given time expires and eventually javascript takes the event from the queue and executes it.
This happens so, because javascript execution is single threaded and they can execute only one thing at a time. So, they cannot execute other javascript and keep track of your timer. That is why these timers are registered with browser (browser are not single threaded) and it can keep track of timer and add an event in the queue after the timer expires.
same happens for setInterval only in this case the event is added to the queue again and again after the specified interval until it gets cleared or browser page refreshed.
Note
The delay parameter you pass to these functions is the minimum delay
time to execute the callback. This is because after the timer expires
the browser adds the event to the queue to be executed by the
javascript engine but the execution of the callback depends upon your
events position in the queue and as the engine is single threaded it
will execute all the events in the queue one by one.
Hence, your callback may sometime take more than the specified delay time to be called specially when your other code blocks the thread and not giving it time to process what's there in the queue.
And as I mentioned javascript is single thread. So, if you block the thread for long.
Like this code
while(true) { //infinite loop
}
Your user may get a message saying page not responding.
And here, your setTimeout event will never execute.
Non-blocking while loop:
let done = false;
setTimeout(() => {
done = true
}, 5);
const eventLoopQueue = () => {
return new Promise(resolve =>
setImmediate(() => {
console.log('event loop');
resolve();
})
);
}
const run = async () => {
while (!done) {
console.log('loop');
await eventLoopQueue();
}
}
run().then(() => console.log('Done'));
Is there a faster alternative to window.requestAnimationFrame() for endless loops that don't block I/O?
What I'm doing in the loop isn't related to animation so I don't care when the next frame is ready, and I have read that window.requestAnimationFrame() is capped by the monitor's refresh rate or at least waits until a frame can be drawn.
I have tried the following as well:
function myLoop() {
// stuff in loop
setTimeout(myLoop, 4);
}
(The 4 is because that is the minimum interval in setTimeout and smaller values will still default to 4.) However, I need better resolution than this.
Is there anything with even better performance out there?
I basically need a non-blocking version of while(true).
Two things that will run sooner than that setTimeout:
process.nextTick callbacks (NodeJS-specific):
The process.nextTick() method adds the callback to the "next tick queue". Once the current turn of the event loop turn runs to completion, all callbacks currently in the next tick queue will be called.
This is not a simple alias to setTimeout(fn, 0). It is much more efficient. It runs before any additional I/O events (including timers) fire in subsequent ticks of the event loop.
Promise settlement notifications
So those might be a tools for your toolbelt, doing a mix of one or both of those with setTimeout to achieve the balance you want.
Details:
As you probably know, a given JavaScript thread runs on the basis of a task queue (the spec calls it a job queue); and as you probably know, there's one main default UI thread in browsers and NodeJS runs a single thread.
But in fact, there are at least two task queues in modern implementations: The main one we all think of (where setTimeout and event handlers put their tasks), and the "microtask" queue where certain async operations are placed during the processing of a main task (or "macrotask"). Those microtasks are processed as soon as the macrotask completes, before the next macrotask in the main queue — even if that next macrotask was queued before the microtasks were.
nextTick callbacks and promise settlement notifications are both microtasks. So scheduling either schedules an async callback, but one which will happen before the next main task.
We can see that in the browser with setInterval and a promise resolution chain:
let counter = 0;
// setInterval schedules macrotasks
let timer = setInterval(() => {
$("#ticker").text(++counter);
}, 100);
// Interrupt it
$("#hog").on("click", function() {
let x = 300000;
// Queue a single microtask at the start
Promise.resolve().then(() => console.log(Date.now(), "Begin"));
// `next` schedules a 300k microtasks (promise settlement
// notifications), which jump ahead of the next task in the main
// task queue; then we add one at the end to say we're done
next().then(() => console.log(Date.now(), "End"));
function next() {
if (--x > 0) {
if (x === 150000) {
// In the middle; queue one in the middle
Promise.resolve().then(function() {
console.log(Date.now(), "Middle");
});
}
return Promise.resolve().then(next);
} else {
return 0;
}
}
});
$("#stop").on("click", function() {
clearInterval(timer);
});
<div id="ticker"> </div>
<div><input id="stop" type="button" value="Stop"></div>
<div><input id="hog" type="button" value="Hog"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
When you run that and click the Hog button, note how the counter display freezes, then keeps going again. That's because of the 300,000 microtasks that get scheduled ahead of it. Also note the timestamps on the three log messages we write (they don't appear in the snippet console until a macrotask displays them, but the timestamps show us when they were logged).
So basically, you could schedule a bunch of microtasks, and periodically let those run out and run the next macrotask.
Note: I've used setInterval for the browser example in the snippet, but setInterval, specifically, may not be a good choice for a similar experiment using NodeJS, as NodeJS's setInterval is a bit different from the one in browsers and has some surprising timing characteristics.
there are some libs that can work like cron task, e.g., https://www.npmjs.com/package/node-cron
i think that using cron should be easier, and more flexible.