Node.js: Callbacks are polluting your code

I have been hacking on a project in Node.js/Express.js for some time now. I am really impressed by how fast it is to code and prototype in Node, not much gets in your way. However, there is one thing I am still struggling with, the callback model of Node.js applications. It is not that it is conceptually hard to understand or use, but i feel that it keeps me from writing clean code.

Let’s imagine that we are writing a small nonsense program. The program receives a post, create some variable x, if some condition is true we call an async method to get some result and assign it to x.result. In the end we want to save x (also async). This would probably be my first attempt in node:



app.post('/someurl', function(req, res) { var x = {..} if (some contidion) { someOperation(function(err, result)) { x.result = result x.save(function(err, result) { if (err) next(err) res.redirect(url) }) }) } else { x.save(function(err, result) { if (err) next(err) res.redirect('/') } }

(Edit: To clarify, both someOperation and save is doing some kind of I/O)

So is this clean code? In my opinion it is not. One problem is that we have the save code in two places. Another is that the code is not very easy to read and understand (remember this is a very simple program and so should the code), but it is not. Sure we could move it to a separate function, or refactor it in some other way, that would help some, but if we are still saving in two places, or if the code is not crystal clear, we are not really addressing the problem.

Let’s instead pretend that we are using synchronous methods (which of course would be a very bad thing in Node.js, so don’t do it) and save are methods that would block:



app.post('/someurl', function(req) { var x = {..} if (some contidion) { x.result = someOperation() //synchronous } x.save() // synchronous return Redirect('/')



To me this is much cleaner and easier to read, saving is only done in one place and the intent of the code is very clear. But why is it so much easier to read? Being synchronous? I wouldn’t say so. The reason is because we do not have to deal with verbose callbacks. In my opinion callbacks are polluting the code. So now you are probably thinking, how can he be programming in Node if he doesn’t like using callbacks? I would say that, at least for the small applications i have build on Node so far, the benefits of simplicity and development speed on Node has totally justified using Node. Even with all the horrible callbacks 🙂 However, in a larger application I am not so sure, it would be very interesting to try out.

So is there anyway we can get rid of the verbose callback syntax? Yes, if some modifications were made to Javascript. But Javascript as a language is not really evolving that fast, so in the meantime it could probably be implemented in a language that compiles to Javascript, like CoffeScript. What we want to do is to be able to do is asynchronicity, but without callbacks. In C# 5 there is a great solution to this problem, the async/await keywords. By using async/await the application could be implemented something like this (pseudo code warning again):



private async Task Post(req) { var x = new ...; if (some contidion) { x.result = await someAsyncOperation(); } await x.save() return new Redirect('/'); }



In short, async tells the compiler that the above method is asynchronous and await will suspend execution of the method until someAsyncOperation is done.

So the above example behaves in exactly the same way as the first example, but with the benefits of the second. In the background a lot of magic is used to construct this behavior, but this is something that the compiler will take care of for us and we don’t have to pollute our code with.

If something like this would be available for Javascript I think it would really make life a lot easier for a lot of Node.js developers. Maybe there is a good solution to this and I am not aware of it (I am far from a Javascript or Node.js expert), if so please enlighten me!

How do you handle flow control in your Node.js applications? How would you have written the example? Any Node.js magic I am missing?