A practical guide to macro tasks, micro tasks and queueMicrotask in JavaScript

kamlesh tajpuri
The Startup
Published in
4 min readJun 14, 2020

--

Let’s have some fun! What will be the output of this snippet?

javascript code snippet to demonstrate micro tasks and micro tasks in javascript.

If you were able to answer this correctly, Congratulations! You have a good idea about the weird parts of JavaScript and event loop. You can safely move on. This post is not for you.

For rest of us, let us find out whats happening here.

Let us for now ignore what queueMicrotask does. What would be the output of below snippet?

The output will be —

setTimeout and Promise are asynchronous tasks and the two top level console logs are synchronous . So here is how the JS Engine will treat this..

Line1 — ok, so you are a simple synchronous console.log. Let’s put you in call stack. 1 is printed immediately.
Line2 — hmm! a setTimeout. Let the browser api handle you. Browser api starts a timer which is specified as second argument. If nothing is provided, it will try to put the callback to the task queue as soon as it is able to do so. Nothing is put in call stack immediately . (setTimeout is not part of ecmascript specs. Browsers implements it because it’s so popular.)
Line5 — ok.. A Promise. Let Me put your callback to the microtask queue. Yes! There is a different type of queue for Promise which is called microtask queue. When the timer of a setTimeout expires, browser api puts the callback in macrotask queue.

Tasks from microtask queue are handled at the end of an execution cycle.They will run after the current task has completed its work and when there is no other code waiting to be run before control of the execution context is returned to the browser’s event loop. The execution will complete only when all the tasks from the microtask queue are completed. A task from macrotask queue will only be put in call stack, if the call stack (execution context stack)is empty. That is the reason why 3 is printed before 2 always in the above code. After printing 1 and 4, first the microtask queue is processed and 3 is printed. Then the script returns hence the undefined. Now it will check if there is some new task available. It can be anything, a button click, user scrolling or setTimeout callback. Here, we have an unprocessed task in macrotask queue, hence 2 is printed now.

Just remember — sync tasks → micro tasks → macro tasks

Now let’s come to queueMicrotask. This function will explicitly put a task in microtask queue. This is what happens when a promise resolves. If you chain multiple then() statements, they will all be put in the microtask queue and are guaranteed to be executed before handling control back to browser’s event loop. So if you keep queuing tasks in the microtask queue forever, your browser will become unresponsive. You can’t click a button, can’t scroll, nothing…, GIFs stop, animations stop etc. The control will never be passed to browser renderer. Don’t believe me? Go to any website, open the console and try this —

You see what happened there?

Now try this one —

You see? The page remains responsive because browser intermittently gets control between the tasks from the macrotask queue. So here is a performance tip, if you have a big sync calculation to do and you can’t use a web worker, consider splitting the task into multiple tasks using setTimeout. That way browser will not become unresponsive!

Now let’s come to the answer to the original question.

1,7 are sync so get printed. The the microtask queue by the main execution context is checked. There are two types of micro tasks here one is the promise callback and the other is task queue by queueMicroTask, which further puts another task in micro task queue. So first 4 is printed, then 5 and as there is still a task remaining in microtask queue 6 is also printed. After this the main execution ends and undefined is printed and the program returns. Now it’s time to look for tasks in macrotask queue. Here the callback of setTimeout will be invoked. 3 is printed as its sync. The the microtask queue is checked again and 2 is printed finally.

Another type of microtask is the callback of MutationObserver.

Thanks for reading.

--

--

kamlesh tajpuri
The Startup

Engineering Manager — React . JS . Node . Perf . People