July 25, 2012

Asynchronous programming

When I discovered the Node.js environment I was pleasantly surprised meeting JavaScript on the server side. I was reading the documentation on its API and encountered the strange for me, at that time, concept of synchronous and asynchronous execution of different operations in this JavaScript implementation. I knew about AJAX and how to work with it, but could not imagine how to create an application based only on the concept of waiting for a response and then make another action.
I made some research and created some simple apps like reading files asynchronously, then I discovered the way to create and organize server-side applications dedicated for a lot of clients, which is hidden in PHP + Apache, which I was used to work with. Some few weeks I was still not used with this new concept, but, after, I realized that it is not so difficult to work with it. It is important to understand that Input / output operations are very expensive compared to data processing, both are vital parts of an applications, but you can separate them by waiting for a response from the I/O operation and doing something else in the main loop. In other words, we have an loop in which our application "is living", it can communicate with a data base, with file system or with clients over TCP/IP, it is waiting for a request from the clients, when it gets one it decide what to do with it and says to the file system to give it the content of a specific file, while it is waiting for the file content it can serve other client requests. All these operations take action in one single process and uses one single core of the CPU, for some specific operations you can use multiple instances of node or some build-in solutions like child processes.
From the point of view of the code, every kind of asynchronous operations are made using process.nextTick(callback) method, it takes as parameter a callback function which will be executed on the next cycle of the main loop. With this method you can directly call some functions after the current cycle ends.

The only difficulty that remains is the complexity of the code that grows as the code itself grows. You can arrive at a structure like this one (Russian doll, Matrioshka):

    A(
        /* ... */
        B(
            /* ... */
            C(
                /* ... */
                DoSomething();
                /* ... */
            )
            /* ... */
        )
        /* ... */
    )

It can be simplified by using function references:

    C( // C definition
        /* ... */
        DoSomething();
        /* ... */
    )

    B( // B definition
        C(); // C call
    )

    A( // A definition
        B(); // B call
    )

    A(); // A call


As you can see this solution does not make the structure of the code so wide and maintain the logical link, also you can use some third-party modules for simplifying asynchrone workflow.

No comments:

Post a Comment