13

We are working with node, mainly for an internal project and to understand the best way to use the technology.

Not coming from a specific asynchronous background the learning curve can be a challenge but we are getting used to the framework and learning the process.

One thing that has polarised us is when the best time to use synchronous code vs asynchronous code is. We are currently using the rule that if anything interacts with IO then it has to be asynchronous via call backs or the event emitter (thats a given), but other items which are not in any way using IO can be constructed as synchronous functions (this will depends as well on the heaviness of the function itself and how blocking it actually is) but is this the best approach to take when working with Node.js?

For instance, we are creating a Hal+JSON builder, which currently exists within our code base. It is synchronous simply because all it is doing is creating some rather smallish object literals and nothing more, there are no external dependencies and certainly no IO interactions.

Is our approach a good one to take or not?

Modika
  • 6,192
  • 8
  • 36
  • 44

3 Answers3

17

Let's say you have two functions, foo and bar, which are executing synchronously:

function foo() {
    var returnValue = bar();
    console.log(returnValue);
}

function bar() {
    return "bar";
}

In order to make the API "asynchronous" is to change it to use callbacks:

function foo() {
    bar(function(returnValue) {
        console.log(returnValue);
    });
}

function bar(callback) {
    callback("bar");
}

But the fact of the matter is, this code is still entirely synchronous. The callback is being executed on the same call stack, and no threading optimizations are being made, no scalability benefits are to be had.

It then becomes a question of code readablity and coding style. I personally find the typical var val = func(); type code more readable and readily understandable. The only drawback is, that if you one day would need to change the functionality of bar so, that it would need to perform some I/O activity or call some other function which is asynchronous, you need to change the API of baras well.

My personal preference: use traditional, synchnous patterns when applicable. Always use asynchronous style when I/O is involved or when in doubt.

jevakallio
  • 35,324
  • 3
  • 105
  • 112
  • I think this is close to where we are now, using synchronous to do smaller items and leaving the async stuff to the I/O bound items. I think also in the example of our builder, it makes sense being Sync at the moment as it doesn't touch I/O and is currently internal, but if we decide to move it into a seperate module we would possibly make it async so that other who work like that can use it in that way. – Modika Dec 13 '12 at 12:44
  • As with the above, this confirmed to me that we are at least on the right track with our thinking (along with some other answers) – Modika Dec 13 '12 at 15:15
  • yes, don't bother with CPS style (async style) unless you are dealing with network or I/O, etc. – Alexander Mills May 20 '15 at 23:06
  • Uhhh, your second function `bar` is NOT asynchronous. It uses a callback, but it's still synchronous. Just like `array.forEach()` uses a callback, but is still synchronous. Not sure how this got to be the accepted answer with so many upvotes. It's wrong. – jfriend00 Feb 25 '17 at 23:09
  • 1
    @jfriend00 thanks for your feedback. If you read the next sentence, right below the code sample, you'll see it says exactly that: "But the fact of the matter is, this code is still entirely synchronous [...]" – jevakallio Feb 26 '17 at 02:31
3

Turning a synchronous function into an asynchronous one using process.nextTick() is an option, but one you should only use if your software is blocking for too long. Every time your synchronous function is running node can't do anything else as it's single threaded. This means that if your application is a server, it becomes unresponsive.

Before you split up any synchronous function, I would advise to first do some benchmarking and profiling so you can make an informed decision. Otherwise you run the risk of dong a premature optimisation

See here for a good discussion https://softwareengineering.stackexchange.com/questions/80084/is-premature-optimization-really-the-root-of-all-evil

Here's how you can make your function asyncronous, in the sense that you will allow node to do other things http://howtonode.org/understanding-process-next-tick

Community
  • 1
  • 1
AndyD
  • 5,252
  • 35
  • 32
1

I don't what's "Hal+Json builder", but here's what I think about NodeJS. You should write your code as asynchronous as possible, pushing it to it's limits. The simple reason is that asynchronous code trades performance for responsiveness, which is in most cases more important.

Of course if certain operations are really quick, then there is no need for asynchronous code. For example consider this code:

var arr = []; // assume array is nonempty
for (var i = 0, l = arr.length; i < l; i++) {
    arr[ i ].doStuff( );
}

If arr is small and doStuff is a quick operation, then you should not bother in writing this code in asynchronous way. However if it takes some time, then you should consider writing it like that:

var arr = []; // assume array is nonempty
var process_array_element = function( ) {
    if (arr.length) {
        arr.pop().doStuff( );
        process.nextTick( process_array_element );
    }
};
process_array_element( );

Now this code is truely asynchronous. It will take even more time for it to complete the job, but in the meantime it won't block entire server. We've traded performance for responsiveness.

Conclusion: It depends on your situation! :)

freakish
  • 54,167
  • 9
  • 132
  • 169
  • The application is a using a Hypermedia API, which we are using HAL+JSON as the type, so we need something to build the responses, it sounds fancier then it is. What we want to do is not optimise before we have to, or even build to a style when its not warranted, so we will always look to Async something that is heavy or IO bound (now or could be in the future), but for smaller, faster, never have IO operations we can safely pump them out as sync code. – Modika Dec 13 '12 at 12:10