Task Queue and Job Queue - Deep dive into Javascript Event Loop Model

There is a popular saying,

Keep every promise you make and only make promises you can keep.

In my last post , I also made a promise that, I shall write about the concept of Task and Job Queues. Here is the post on it. What is actually very interesting is, this post is mostly about How to keep a Promise and execute it.

Before we move further, I would like to clarify that, I am not going to explain about Promises as a concept here. There are plenty of good reads on it. Here is my personal favorite. However this particular post is about understanding when your promise gets executed and why, than the definition of Promise itself? Along with it, we will also learn the difference between Task Queue and Job Queue.

To recall, some of it already explained here that, there is Task Queue in Event Loop Model. There is a Job Queue as well. Here are few important points to note:

  • Not all the tasks are created of same priority. There are macrotasks and microtasks.
  • MacroTasks are called, Tasks and MicroTasks are called, Jobs.
  • Examples of Macrotasks are, setTimeout, setInterval, setImmediate, I/O tasks etc.
  • Examples of Microtasks are, Promises, process.nextTick etc.
  • The Queue in Event Loop Model holds the Tasks(or the MacroTasks) called, TaskQueue.
  • The Queue in Event Loop Model holds the Jobs(or the MicroTasks) called, JobQueue.
  • For example, Promises are in Job Queue and the functions for setTimeOut are in TaskQueue.

    With the explanation above, let us re-visit the Event Loop Model once more than last time .

eventloopmodel.png

The obvious question would be, how does the event loop decides which queue to de-queue from and push to Call Stack when the Stack is empty? The answer depends on this logic(or, set of rules):

  • For each loop of the 'event loop' one macrotask(Task) is completed out of the macrotask(Task) queue.
  • Once that Task is complete, the event loop visits the microtask(Job) queue. The entire microtask(Job) queue is completed before the 'event loop' looks into next thing.
  • At any point of time if both the queues got entries, JobQueue gets higher precedence than TaskQueue.

Overall, the Event Loop got one more item to consider in its orchestration of Code Execution. Let us understand the above logic with a Code execution flow.

const tom = () => console.log('Tom');

const jerry = () => console.log('Jerry');

const cartoon = () => {
  console.log('Cartoon');

  setTimeout(tom, 5000);

  new Promise((resolve, reject) =>
    resolve('should it be right after Tom, before Jerry?')
  ).then(resolve => console.log(resolve))

  jerry();
}

cartoon();

So the expected output is,

Cartoon
Jerry
should it be right after Tom, before Jerry?
Tom

Let me explain Why?

  • The function cartoon gets into the Call Stack.
  • It executes the console log of the text Cartoon.
  • In the next line of execution, the setTimeOut web API goes outside of the Call Stack and the associated function tom gets place into TaskQueue.
  • In the next line of execution, we encounter a Promise. A callback of a promise gets place in JobQueue. Hence the console log function execution on the promise goes to JobQueue.
  • In the next line of execution, the function jerry pushed into the Stack and gets executed.
  • Now the fun begins. We have one entry in TaskQueue and one in JobQueue. The Event Loop Model, gives priority to all the Jobs in JobQueue over anything in TaskQueue. Hence the callback of the promise get to the Call Stack first, gets executed and then, the function tom get to the Call Stack from TaskQueue and gets executed.

That's all about it. I hope, you got the core concept. Now, Here is a puzzle for you. Let me know, what is the expected output of this code execution? Feel free to post a comment with your answer.

const tom = () => console.log('Tom');
const jerry = () => console.log('Jerry');
const doggy = () => console.log('Doggy');

const cartoon = () => {
  console.log('Cartoon');

  setTimeout(tom, 50);
  setTimeout(doggy, 30);

  new Promise((resolve, reject) =>
    resolve('I am a Promise, right after tom and doggy! Really?')
  ).then(resolve => console.log(resolve));
  new Promise((resolve, reject) =>
    resolve('I am a Promise after Promise!')
  ).then(resolve => console.log(resolve));

  jerry();
}

cartoon();

Tips: if you hear any of your friends talking about another queue called Message Queue, don't be upset. They are just referring to Task Queue just by another name.

Hope you liked the post. Cheers!

Tapas Adhikary's photo

Tapas Adhikary

Write your comment…

Thanks for great Article.

Cartoon

Jerry

//These micro task has precedence over macro tasks

I am a Promise, right after tom and doggy! Really?

I am a Promise after Promise!

//Finally macro tasks

Doggy

Tom

Show all replies

oops yes your are correct

Reply to this…