Image credit: Jsmodular

Javascript event loop explained

Image credit: Jsmodular

Javascript event loop explained

Introduction

Javascript is a single thread language by default. However, as web world moves fast, a single thread cannot undertake heavy tasks. It’s hard to change the fundamental mechanism from a single thread to multi-threads. To solve this problem, people come up with the concept of event loop. The concurrency model of javascript is based on event loop [1]

Event loop

Prior to explaining the event loop. It’s better to understand something called block I/O and non-block I/O. Suppose the application you are coding is a single threaded application, whenever you send a request to the Internet or read files from the local file system. It blocks, which means the rest of code can only be running after the request finished. for example:

import requests
// this is i/o request
val = requests.get('http://example.com')
print(val)
requests.get('http://example2.com')
renderPage()

Line 3 will run first, depends on the network, we may wait milliseconds or seconds, before the fetch finish, the rest code cannot be executed, which obviously is inefficient, because code print(val) may depends on the result above. However, the rest code does not, but you still have to wait until the request finished to execute the remaining code.

So how does event loop work? Here is an image shows the fundamental concept: eventloop

To make it simple, every time when you do any I/O task, this task will be set as an event and put inside an event queue then the rest code will be executed as normal. After you run out all your functions in your call stack. Event loop will come to the event queue to check if there any job has not been done, if so, event loop will fetch the event inside the queue based on priority (macro and micro event, we will talk it in the next section). There is a event loop visualizer created by Philip Roberts [2].

To make it concrete, here is an example code.

setTimeOut(function(){
  console.log("event 1");
}, 1000);

setTimeOut(function(){
  console.log("event 2");
}, 2000);

both setTimeOut will be put inside an event queue, waiting to be executed. Event 1 will be executed after 1000 milliseconds (1 second), event 2 will be executed after 2000 milliseconds (2 seconds). Actually, it is not precisely 1 second or 2 seconds, we will discuss the reason later on.

Macro task vs Micro task

Have a look following code, think about what will be printed

setTimeout(function(){
  console.log(1);
}, 0);

new Promise(function(resolve, reject){
  console.log(2);
  resolve(3);
  console.log(4);
}).then(function(value){
  console.log(value);
})

console.log(5);
## 2
## 4
## 5
## 3
## 1

The output is surprise right? Let’s analyze it,

Both setTimeout and Promise are events so that they will be executed asynchronously. However, since promise is a micro task which has higher priority than setTimeout (macro task), even we have setTimeout is 0, promise will run first, the code inside promise will run first, therefore 2 is printed first, resolve is a callback function, normally resolve will wait for the result of an async call. Therefore, the code will continue running to print 4 and return a promise back then we have console.log(5) which will print number 5. Afterwards, the callback of promise running which prints number 3, finally number 1 will be printed.

Javascript event has two categories

  • Macro task
  • Micro task (higher priority)

By default, when the single main thread encountering functions (sync), it will put functions inside call stack, execute them then pop them out, if the function running for a long time without returning value, the main thread has to wait there. This is why single thread not effcient. However, javascript will make the time consuming task (I/O) return a placehold value first. The I/O task will be send to browser kernel, when task finished, task and its callback functions will be send back to task queue. The advantage of this strategy is that the main thread is able to continue running without blocking, after the main thread run out off all the functions that inside the call stack, the main thread will ask the task queue for more jobs to do, once there has any task, it will be pushed into the call stack execute, pop up. This kind of steps will be running in a loop, that’s why we call it event loop.

Macro task

  • SetTimeout
  • SetInterval
  • SetImmediate
  • ajax

Micro task

  • async/await
  • promise

Let’s do a crazy test to see if we really understand event loop. What will be printed the following code?

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}

console.log('script start');

setTimeout(function() {
    console.log('setTimeout');
}, 0)

async1();

new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');
## script start
## async1 start
## async2
## promise1
## script end
## async1 end
## promise2
## setTimeout

So let’s demystify the code above, first of all, async function is essentially return a promise. However, async1 and async2 are just defined here not called. Therefore script start print first. Then setTimeout is another async function which has lower priority. Afterwards, async1() is called, async1 start print first, async2() is another promise, the code inside will be printed, hence, async2 was printed. Next, we have another promise, this time promise1 was printed, since resolve() is a callback, the code will continue running. script end prints next. Let’s trace back what we have left, async1() has not finished yet. Therefore, async1 end will be printed and our callback of promise has not finished yet, promise2 will be printed. Finally, the macro task setTimeout will be printed.

Reference

1. Concurrency model and Event Loop [Internet].MDN Web Docs2019;Available from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

2. 2014;Available from: http://latentflip.com/loupe

Avatar
Terry Pan
Student of Data Science

My research interests include Machine Learning, Data Science, Information Security and Software Engineering. I like to think like a engineer to tackle real world problems.

Related

comments powered by Disqus