HiveBrain v1.2.0
Get Started
← Back to all entries
debugjavascriptMinor

Writing a user's profile image to disk using nested Node.JS closures

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
imagediskusernodewritingprofilenestedusingclosures

Problem

I am writing non-blocking code as it should be in Node.JS. If I want run function A after B I do it using callback.

But I find this style guide:


Use closures, but don't nest them. Otherwise your code will become a
mess.


Right:

setTimeout(function() {
  client.connect(afterConnect);
}, 1000);

function afterConnect() {
  console.log('winning');
}




Wrong:

setTimeout(function() {
  client.connect(function() {
    console.log('losing');
  });
}, 1000);


How deep should my closures be? Specifically, I'd like to consider this sample:

exports.profileImage = function(req, res) {
    var id = req.params.user_id;
    userProvider.get(id, function(err, user){
        if (err) throw err;
        userProvider.getImageById(user['image_id'], function(err, image) {
            if (err) throw err;
            userProvider.writeImageToDisk(image, function(err, path){
                if (err) throw err;
                res.sendfile(path);
            });
        });
    });
};

Solution

First of all, you are correct. One way to prevent deep nesting is to pull out the callback into it's own named function. This allows you to only be as deep as 2-3 levels in blocks of code.

function task1(){
  async1(task2);
}

function task2(){
  async1(task3);
}

function task3(){
  async1(task4);
}


Another way to do it is by using flow-control libraries which take advantage of the concept of Futures/Deferreds/Promises. There are a lot of ways to call them, but they are the same thing most of the time.

The basic concept is that they usually stack up callbacks via utility functions and then run them, depending on the result of the previous or depending on the used utility function. The code will look linear and synchronous, but it actually is running async and the library controls the flow of calls and results. In jQuery, you can do something like

//Run an addition in the server
$.get('add.php',{
    op1 : 1,
    op2 : 2
  })
  .then(function(result){
    //get the addition result (3) and multiply it on the server (another async)
    return $.get('multiply.php'{
      op1 : result,
      op2 : 3
    });
  })
  .then(function(result){
    //get the multiplication result (9) and divide it on the server (another async)
    return $.get('divide.php'{
      op1 : result,
      op2 : 9
    });
  })
  .done(function(result){
    console.log(result); //1
  });

Code Snippets

function task1(){
  async1(task2);
}

function task2(){
  async1(task3);
}

function task3(){
  async1(task4);
}
//Run an addition in the server
$.get('add.php',{
    op1 : 1,
    op2 : 2
  })
  .then(function(result){
    //get the addition result (3) and multiply it on the server (another async)
    return $.get('multiply.php'{
      op1 : result,
      op2 : 3
    });
  })
  .then(function(result){
    //get the multiplication result (9) and divide it on the server (another async)
    return $.get('divide.php'{
      op1 : result,
      op2 : 9
    });
  })
  .done(function(result){
    console.log(result); //1
  });

Context

StackExchange Code Review Q#35307, answer score: 4

Revisions (0)

No revisions yet.