programacion-asincronia-concurrencia

What is asynchronous programming

  • 4 min

Asynchronous programming is a set of techniques that allows applications to perform multiple tasks simultaneously, without blocking the main execution flow.

If your computer only did one single thing, it wouldn’t be very useful. Imagine that every time you open a file, or visit a web page, your computer would “freeze” until it finished. That wouldn’t be very practical!

In fact, your computer does thousands of tasks “simultaneously”. It handles WiFi, paints the screen, reads the hard drive, runs a program, and reads the keyboard. It does all that “more or less” at the same time.

programacion-asincronia-concurrencia

Your CPU, juggling processes

In the same way, you must prepare your programs to do several things “at once”. That’s what we call asynchronous programming, creating programs that are capable of functioning in a not entirely sequential manner.

Advantages of Asynchronicity

Why is it important to add asynchronicity to your programs? Well, there are several reasons and advantages associated with being able to execute tasks in a non-sequential way.

The main reason to apply asynchronicity is the improvement of the user experience in the form of responsiveness. That is, in giving the user a response through the user interface.

Without asynchronicity, for example, your program couldn’t even show a “loading” window with a spinning circle while loading data from somewhere. It would get blocked and you couldn’t do absolutely anything.

The other major reason is that, under certain circumstances, it can mean an improvement in resource efficiency (resources that are limited and can become a bottleneck).

For example, imagine that the processor itself is idle, waiting to receive a response from a website. With asynchronicity, it is possible to take advantage of that wait to “insert” other calculations in between.

Difficulties of Asynchronous Programming

Of course, not everything was going to be advantages. Asynchronous programming is objectively more difficult than sequential or synchronous programming. It always has been, and it always will be.

This is simply because it is inherently more complex. It will always be more complicated to handle several things at once, than just one (just like juggling five balls is harder than juggling one).

One of the problems you will have to consider is temporal synchronization. Now one thing doesn’t happen after another, but they occur in parallel or semi-parallel.

So your mind must stop thinking of your program as a series of tasks that execute sequentially, and start thinking of a timeline with several lines of execution that can be parallel and interfere in a non-deterministic way.

Another big problem is access to shared resources. If a resource is being actively used by one process, it is very likely that another process cannot use it. If both try to access it, you will probably have an error.

Finally, you will also have difficulties sharing memory. It is even possible that one process is “doing its thing” and another process modifies one of its variables. Or that one task cannot access the memory of another.

We didn’t have any of these problems in synchronous programming. To manage these things we have created a lot of tools and resources, like mutexes, semaphores, monitors… but even so, you will always have added complexity in the design.

Mechanisms for Asynchronous Programming

In addition to the tools I have already mentioned, different programming languages have incorporated various mechanisms to make implementing asynchronicity easier.

Some of them are:

  • Callbacks / Events: Functions that are passed as arguments to other functions and are executed after an operation completes.
  • Promises / Futures: Objects that represent a value that may be available now, in the future, or never.
  • Async / Await: Syntax that allows writing asynchronous code in a way more similar to synchronous code.
  • Threads: Threads allow executing multiple tasks in parallel within the same process.
  • Coroutines: They are functions that can pause their execution and yield control to other coroutines.
  • Event Loop: A mechanism that allows handling input/output operations in a non-blocking way.

These mechanisms provide various ways to manage the execution of concurrent and asynchronous tasks, each with its own advantages and disadvantages.

Of course, the choice of the appropriate mechanism depends on the programming language and the type of application you are developing. (we will see them in depth in the various articles of this chapter).