Understanding Event Loop

Event Loop is one of the most misunderstood concepts in Javascript. Since javascript is single threaded, it is still able to perform concurrent and async operations. Such operations are traditionally performed using threads in Ruby or Java systems. Through this post I wish to clear out some of the doubts on how Javascript performs async operations.

Event loop has been the key concept in non blocking I/O which has found its way from the browser to server in the form of Node.js. In order to understand event loop, it is important to understand how Javascript runs in general.

Call stack

When javascript runs a piece of code, it keeps reference of the current executing function in a stack which is called the call stack. When the function finishes execution it is removed from stack.

callStack

In the above example we have three functions (fn1, fn2, fn3). Following is the sequence of steps when javascript executes the code:

  1. Start execution using the main thread referred to as main() here.
  2. Identify the function which needs to executed and add it in the call stack.
  3. Identify other function which the current function executes and add it in the call stack.
  4. Upon completion of the function remove its reference from the call stack.
  5. Repeat steps #2 to #4 till the code finishes execution.
  6. After all the code has finished execution, remove main() from the call stack.

We can see call stack in action in browser when we get a javascript error.

Screen Shot 2018-01-20 at 1.31.06 PM

Here browser shows the call stack which lead to javascript error. This allows us to debug the code.

Single thread

Javascript doesn’t have the concept of threads. It has a single thread which executes code in sequential manner. It means that any function which takes a lot of time can block thread execution. When thread execution is blocked, browser cannot perform any function such as take user inputs. Essentially the page freezes. However, any non trivial application has computation intensive code. So there has to be a mechanism to handle such operations.

Async call stack

To solve this we write asynchronous code which performs time consuming operations in the background without blocking the main thread and uses callback mechanism to resume code execution. setTimeout is one such mechanism.

asyncCallStack.gif

setTimeout is one of the mechanism to write async code in Javascript. In the flow above, we have a setTimeout which will run the function passed to it after at least 5 seconds. The main thread executes setTimeout sequentially just like any other function call. Hence, the call the stack is similar to our previous example. However, after 5 seconds, the setTimeout’s callback magically comes into picture and gets added in the call stack. It means that Javascript wasn’t just executing the actual code, it was also keeping track of when to call the setTimeout callback. Clearly, this violates our assumption that Javascript being single threaded can do only one thing at a time. There is more to it that meets the eye. The magic referred to here is performed by the event loop.

Event loop

When we say Javascript is single threaded, we are referring to the Javascript runtime (for eg. V8, Blink). The event loop is what browsers provide. Following is the basic anatomy of a browser:

browser.png

  1. Javascript Engine: Javascript engines like V8, Blink provide the javascript runtime. It contains the function call stack and memory heap. Memory heap is used to maintain references to objects and functions required by call stack.
  2. WebAPIs: APIs like setTimeout, setTimeInterval are provided by the browser. These APIs are responsible to add callbacks in event queue. For eg. setTimeout(cb, 5000) will add the callback function cb  in event queue after the 5 seconds.
  3. Event Queue: Event queue contains functions which are to be added in call stack by event loop.
  4. Event Loop: Event loop is responsible to push the functions available in event queue to the main call stack. Event loop pushes a function only when the call stack is empty.

eventQueue.gif

Important thing to note here is that event loop will only add a call back from the event queue when it finds the call stack as empty. If Javascript’s main thread is busy executing a long running function, it would keep the callback in queue. The broader implication of this is that if we have long running functions which block the main thread, Javascript will not be able to respond to user events as they would be stuck in event queue.

 

Conclusion

Event loop is browser’s mechanism to perform non blocking operations. It does that by providing webAPIs (eg. setTimeout) which are capable of maintaining callback references in memory. The callbacks are added to event queue depending on the logic of  the webAPI (for eg. setTimeout waits for n milliseconds, XMLHTTPRequest waits for ajax call to finish). Event loop adds callback from the event queue and pushes it to the call stack where the callback is executed.

Bidirectional vs Unidirectional data flow architecture

MVC (Model, View and Controller) architecture has been in the industry for quite sometime. This pattern has served web applications well by decoupling their various components. The following diagram gives a high level idea of how interaction between different components work.

mvc.png

Controller is a broker between View and Model. The flow of data happens between model-controller and controller-view. Since view and model don’t have a direct communication hence they are unaware of each other. This allows us to change them independent of each other.

With the advent of complex frontend heavy applications (eg. gmail), this pattern found its way in javascript as well. The above diagram can be mapped to javascript applications as follows:

mvc4.png

On front end, data flows from the view to server due to user interactions (for eg. button click). Similarly data flows back to the view from the server in case of server updates.

For applications which have thin front end (i.e. no business logic in JavaScript), the role of controller is that of a data binder between model and view. For instance,


//Send form data to server on click of button
$('#clickMeBtn').click(function(){ form.submit();})
//Update view with data recieved from server
$.get('/get/employee', function(employee){
$("#employeeName").text(employee.name);
})

The above code does the basic functionality of relaying data back and forth between view and server.

However, for an application with good amount of user interaction adding such code can become repetitive and combursome.  In such scenarios, we can leverage AngularJS’s two-way data binding. This allows us to declaratively bind the view with model thereby relieving controller of this duty. For such thin applications, custom controllers don’t play a significant role and are just a support function to AngularJS.

mvcAngularJS.png

Hence, in a thin javascript application with considerable amount of user interaction we can have a bidirectional data flow directly between the view and server.

Things start becoming interesting and complex for applications which have thick front end. Such applications have a good amount of business logic on the client side as well. One case of adding business logic on client side is to make the application seem highly nifty and responsive. This leads to higher user engagement and retention. For eg. social networking sites like Facebook and Twitter have thick front ends as they want to give a user instant feedback and keep them engaged.

In such applications, controllers start to play a significant role. As a result, they come out of the shadow of AngularJS and start showing their influence on View and Model.

mvcAngularJSController.png

The above diagram shows that data can flow between view and model from either the controller or AngularJS. This kind of architecture gives us low confidence on how the view and model interact. We can never tell with surety if the view got updated logically or as a side effect by AngularJS.

In an application, view is a function of the data provided to it by the server. Mathematically speaking,

viewAsFn.png

Mathematical functions are pure functions, which means that a given input maps to the same output all the time.

In the current architecture, we cannot say with a high degree of confidence that given the same data from server will our application show the same view. This is due the fact that some parts of our view change which are not in our control (or not easy to reason about).

In such thick frontend applications, we have to introduce the concept of unidirectional data flow.

dispatcher.png

Here, view talks to a component called Dispatcher through actions. Actions are generated through user-generated events like click or keypress. Dispatcher figures out what data this action needs to update and sends it to the Application Store for further processing. Application Store contains all the state information of the application. It can also communicate with the server for updates. Updates to the store are listened by the View which in turn updates itself.

Since the flow of data is unidirectional, there is only one way through which view can be updated. As a result this gives us a high degree of confidence that our view is a function of data and hence is deterministic.

The above architecture is as simple as it looks. We can find variations of this architecture in the form of flux, redux and elm.

The implementation of this architecture can become a bit more involved as compared to an out of the box bidirectional data flow architecture. However, this gives us more control over how data flows in our application.

Conclusion

For thin front end applications with minimum business logic in JavaScript, we can deliver quickly with bidirectional data flow in the form of two-way data binding provided by AngularJS. However, for thick frontend applications we should rely on unidirectional data flow to get more confidence regarding how the view will be displayed based on the state of the application.

Further reading

The case for unidirectional data flow

Unidirectional User Interface Architectures

Books to read in 2018

For 2018, I have shortlisted 20 books to read from my never ending Want to Read list. The books are categorised in areas of my interest.

Software

  1. Coders at Work: Reflections on the Craft of Programming
  2. Continuous Integration: Improving Software Quality and Reducing Risk
  3. The Art of Capacity Planning: Scaling Web Resources
  4. The Cathedral & the Bazaar: Musings on Linux and Open Source by an Accidental Revolutionary
  5. Build Your Own AngularJS
  6. Clean Architecture
  7. Software Architecture in Practice 3/e
  8. Clean Code
  9. Deep Learning
  10. Code: The Hidden Language of Computer Hardware and Software
  11. Understanding Ecmascript 6: The Definitive Guide for JavaScript Developers
  12. The Nature of Code

Management

  1. Better Under Pressure: How Great Leaders Bring Out the Best in Themselves and Others
  2. The Manager’s Path: A Guide for Tech Leaders Navigating Growth and Change
  3. Herding Cats: A Primer for Programmers Who Lead Programmers

Business

  1. Energy and Civilization: A History
  2. The Start-Up of You: Adapt to the Future, Invest in Yourself, and Transform Your Career

Science

  1. Physics of the Future: How Science Will Shape Human Destiny and Our Daily Lives by the Year 2100

Tech Industry

  1. The Four: The Hidden DNA of Amazon, Apple, Facebook, and Google

Others

  1. Man’s Search for Meaning

Reactive programming vs Passive programming

Reactive programming has been quite a buzz word for quite sometime. It is often confused with programming in React.js. In this post I will share my understanding of what is reactive programming and when to use it.

Software programming is about implementing a business use cases in a language which computers can understand. High level languages have made programming easier and accessible for everyone. This has allowed us to tackle complex business problems such as e-commerce, health insurance. However, it is important to distinguish complexity into essential and accidental.

Accidental Complexity

Accidental complexity is the complexity introduced in a system by the architecture and programming paradigms. This is generally independent from the business problem.

Consider the following code snippet in Java to read a file:

InputStream fileInputStream = new FileInputStream(FILE_PATH);
BufferedReader bufferedReader = new BufferedReader(
        new InputStreamReader(fileInputStream));
String line="";
while((line = bufferedReader.readLine()) != null){
    System.out.println(line);
}

Following is the code in Python:

with open(FILE_PATH, "r") as file:
    for line in file:
        print(line)

Python achieves the same functionality with lot fewer lines of code. This complexity is not due to any business requirement. Solving such complexity is generally straight forward. In the above case, we can use class composition to reduce the lines of code.

Essential complexity

Some systems are inherently complex and take time to understand. Consider the following system:

delhi-metro-rail-map copy

The above image is a route map of Delhi Metro in India. Looking at the above map, it is difficult to find the exact route from station A to station B. There can be multiple alternative routes and some of them might not be valid. However, a color coded image can remove majority of confusion:

delhi-metro-rail-map

Now we can get a better clarity of how many different lines of trains ply in Delhi Metro. It binds stations on the same line much more clearly. Hence, solving an essential complexity involves finding creative solutions which make the complexity easier to understand. This is where the understanding of reactive and passive programming can help us.

A software is divided into modules with each performing a set of cohesive functionality. I will take an example of e-commerce checkout flow system. Consider two modules, Cart and Invoice such that if an item is added in Cart, Invoice should be update.

CartItem.png

One way to achieve this is to add dependency of Invoice on Cart, such that when Cart updates itself, it updates Invoice as well. Something like the following pseudo code in Cart:

addItemInCart(Item item){
    Invoice.update(item.price)
}

CartInvoice1

Another way to achieve this is to add dependency of Cart on Invoice such that Invoice updates itself when an item is added in Cart. Something like the following code in Item:

Cart.onAddItem(function(Item item){
    self.update(item.price);
});

The above code uses a callback function  which gets invoked when item is added in cart.

CartInvoice2.png

Notice the difference between the arrows in the above two figures. In case when Invoice is passive, Cart is dependent on Invoice. Hence the arrow is near Cart. In case when Invoice is reactive, Invoice is dependent on Cart. Hence the arrow is near Invoice. This is the major difference between reactive and passive programming. In reactive programming, a module is responsible to update itself as compared to passive programming, where other modules are responsible to update the module.

In a fairly complex system, managing dependencies is a critical aspect. Systems with clearly defined dependencies are easy to refactor and understand. For a module, dependencies can be described in two areas:

  1. How the module works?: A module will be dependent on other modules to complete its functionality. In our example, Invoice depends on Cart to update itself.
  2. What does it affect?: A module might affect modules which are dependent on it. In our example, Cart affects Invoice.

Hence a comparison of reactive vs passive programming in these two dimensions results in the following table:

Passive Reactive
How the module works? Find usages Look inside
What does it affect? Look inside Find usages

In case of reactive programming, we need to look inside the code of module to find out how it works as the module is responsible for itself. Similarly in passive programming, we need to find usage of a module across other modules as other modules are responsible to update it.

Dependency.png

Scanning through other modules is a difficult job. Incase the module is accessible publicly, it might not be possible to find out its reference. Hence a benchmark of when to use passive vs reactive programming is the ease of how vs what.

In general, software systems become complex when we don’t know how our modules work. Invoice can be updated by Cart but in future might be updated by  Discounts and Taxation modules. Reactive programming serves us well in such scenarios.

Further reading:

  1. Reactive Programming: Why It Matters
  2. The Reactive Manifesto

 

Thoughts on being a senior engineer

Having worked for almost a decade in software engineering, I generally pause and reflect on my journey. Have I produced enough value? Have I gained enough knowledge? Am I considered an expert in the field I work in? If I start over will I end up in the same position as I am now? What do my fellow engineers expect from me?

I have noticed that all these questions have a common theme. These questions are with respect to what others see me as. Such questions have no straight forward way of measurement, unless you have a mentor who can give unbiased opinions.

At times it becomes difficult to proceed in our careers without getting answers to these questions. Some engineers end up switching roles and move to management. However, I am of the opinion that they take some of these questions with them in the new role.

Although I don’t have answers to many of these questions, I do follow some practices which have helped me perform well in different roles through out my career. I wish to share some of them here:

  1. Always take out time for others: As a senior engineer, I have observed that fellow engineers want to bounce ideas off me. At times it’s for validation of an idea or to get a different perspective. I find such exercises really helpful. Even if I am in the middle of something, I try my best not to miss out on such opportunities. These small discussions are full of energy and enthusiasm. And I have seen otherwise introvert people opening up to me. Such exercises not only help others but help me as well. I can reach out to anyone when I need to bounce off ideas without the awkward small talk or ice breakers.
  2. Always do the right thing: A recurring thing which I have learnt from experience is that deep down, my subconscious knows what is the right thing to do in any situation. To meet a deadline, I have known that it is right to work on weekends or stretch yourself couple of hours in office. I have known that it is right to report or fix a glaring bug rather than waiting for quality analyst to do so. However, many times I have done the wrong things in such situations, may be out of laziness or out of pride and regretted in most of the times.
  3. Adopt a growth mindset: I have always wanted to work on the most challenging aspect of any project. Earlier in my career, I used to feel a bit disappointed when I was not approached first for such complex items. As a result, I either distanced myself too far from these items or prayed for them to fail spectacularly so that I can give a go at them and come out victorious. In almost all the cases, it used to be the former. I have realised that distancing myself from such situations help nobody. I miss an opportunity to contribute on something complex and people miss on my inputs. Now, I try to adopt a growth mindset by just asking if I can be involved in these projects. This has allowed me to be involved in challenging projects and people find me quite approachable as well.
  4. Create a schedule and stick to it: Early in my career, I didn’t have any schedule in place. I didn’t know where my time goes. I generally had the complaint that I don’t have much time to do the important stuff, even though I didn’t have much roles and responsibilities outside of work. To fix this, I started creating a schedule on my calendar by allocating time for things like reading blogs, reading news, watching an online course. This worked well till the moment I found a new activity which should be part of my schedule, for eg. solve one algorithm/ds problem daily. This involved rethinking about the schedule, removing some activity or reducing time from another activity. These iterations happened quite frequently, sometimes every other day. This led to frustration and confusion. I have realised that each schedule should be given a chance of almost 4 months to figure out if it works or not. This allows me to know with certainty what is feasible and what is not.
  5. Take care of your health: Software engineering is a profession which involves creativity and problem solving. This requires one to be alert and active throughout the day (at least during office hours). I have observed that my level of enthusiasm is directly proportional to how I am feeling health wise. I need energy to implement any new ideas I have in my mind. Hence, it is important to keep tab on what I am eating and have physical exercise every day.

These are some of the things I keep in mind in my everyday. Following these keep the nagging questions at bay for some time!

Thanks for reading!