Should I use Asynchronous Programming?

Many developers are given the task of optimizing application code for efficiency and too often, I have seen developers getting drawn towards asynchronous programming only by hearing the magic words — “Asynchronous programming allows you to run code concurrently”, they start to believe that multiple parts of their code will be able to run in parallel, enabling them to execute multiple tasks together. While this is certainly possible, it is important to understand what parts of the code are eligible to run together.

I will be talking about how to recognize when your application can benefit from asynchronous programming and also when it won’t make any difference. For that, you must understand the concept of asynchronous programming. The simplest way by which I explain it to new discoverers is that —

“Think of a fly buzzing on the surface of your screen in a circle around your code. This fly jumps from function to function that you have defined and executes only a few lines of each function and then jumps to another and repeats this over the same functions again and again till they finish execution completely. It basically gives ‘Processor time’ to a function for a while and then passes it to the next function, allowing concurrent sharing of Processor without making the functions wait for completion of other functions. And this passing of ‘Processor time’ can be scheduled by the programmers at times when the function being executed no longer needs the processor”

The circle it’s making called an event loop. Relax it’s not an actual fly.

This is where people stop understanding the concept behind asynchronous programming. They say — “A function stops needing the Processor only when it finishes execution, so when and how will all functions share it?”

The answer is that a function might not always need a Processor even while executing, it sometimes needs to — read input from the disk (e.g. read from a file), fetch rows from a database or get a response from an API call over the network. The Processor is free during this time. How? It is free when the disk is rotating and also when the data is coming via the internet. If it takes 5 milliseconds for the disk or network I/O to finish, the Processor sits idle for that time. If there was another function that needed less than 5 milliseconds to execute, it could have finished execution during that wait time if it had the processor.

So, the “eligibility” to run together, which I mentioned earlier should be understood in a sense that two functions can run together if both can share the CPU when they are waiting for disk or network I/O.

As an example, let’s see two independent functions A & B, which take 5 seconds each to execute. Out of these 5 seconds, 2s are for CPU and 3s are for I/O. Normally they would take 10s to run together. But if during the I/O of A when CPU is free, if B could get CPU, the total time would be 8 seconds (shown below). These savings add up a lot in real time applications and help in scaling efficiently by optimizing CPU usage on servers. This is the reason why servers written in Node.js, Go, Sanic, etc. are able to handle a large number of requests.

Function B finished early

One warning — Functions can share CPU whenever we program them to. But the sequence has to be programmed correctly to ensure they share it efficiently. We can even ask two functions which take entire 5 seconds of CPU time to share CPU but it will not only end up taking 10 seconds but can even cause delays in execution of functions that were called first.

Both functions finish very late

Asynchronous Programming helps to optimize applications for significant gains in efficiency if the applications have I/O bound tasks like database querying or API calls. It can, although, make the code very complex (by adding a lot of callbacks as in Node.js) which makes it less readable. The points discussed in this article are only the entry points while considering going asynchronous. If these conditions are not there in your application, it’s better not to proceed with it.

There are many pitfalls like losing simplicity, out of sync data, single server bottlenecks, etc. I’d go into each separately if demand is significant. Meanwhile, I’d recommend you to get your hands dirty and try it out separately. I can guarantee you’ll love it.