Jack Johnson
Jack Johnson

Reputation: 1525

How to handle callbacks in Node.js?

Let's say I have 3 files.

index.js makes a call to the backend like this

$.post('/test/', data, function(response) {
 //handle success here
})

routes.js handles the route like this

app.post('/test/', function(req, res){
   item.getItems(function(response){
     res.json(response);
   });
 });

items.js is the model which accesses the database and makes a POST request for each item

function getItems(callback) {
  database.query('SELECT * from items', function(result){
    result.forEach(function(item){
        request.post('/api/', item, function(req, res) {
          //finished posting item
        });
    });   
  });
  //callback here doesnt wait for calls to finish
}

where/when should I call the callback passed to getItems() to handle a success/failure in index.js?

Upvotes: 1

Views: 200

Answers (2)

jfriend00
jfriend00

Reputation: 707158

Because your request.post() operations are async, you have to use some method of keeping track of when they are all done and then you can call your callback. There are multiple ways of doing that. I'll outline a few:

Manually Keeping Track of Count of Request Operations

function getItems(callback) {
  database.query('SELECT * from items', function(result){
    var remaining = result.length;
    result.forEach(function(item){
        request.post('/api/', item, function(err, res) {
          --remaining;
          //finished posting item
          if (remaining === 0) {
              callback();
          }
        });
    });   
  });
}

The main problem with doing this manually is that propagating error in nested async operations is difficult when you get to actually figuring out how you're going to handle errors. This is much easier in the other methods shown here.

Using Promises

// load Bluebird promise library
var Promise = require('bluebird');

// promisify async operations
Promise.promisifyAll(request);

function queryAsync(query) {
    return new Promise(function(resolve, reject) {
        // this needs proper error handling from the database query
        database.query('SELECT * from items', function(result){
            resolve(result);
        });
    });
}

function getItems(callback) {
    return queryAsync('SELECT * from items').then(function(result) {
        return Promise.map(result, function(item) {
            return request.postAsync('/api/', item);
        });
    });
}

getItems.then(function(results) {
    // success here
}, function(err) {
    // error here
})

Upvotes: 2

incutonez
incutonez

Reputation: 3321

It seems strange that you're making an API request in your server-side code, unless this is some sort of middle tier code that interacts with the API... but you're interacting with a database, so I'm still confused on why you can't just do a database insert, or have a bulk insert API call?

Anyway, if you must do it the way you're asking, I've done this in the past with a recursive method that trims down the result array... I really don't know if this is a good approach, so I'd like to hear any feedback. Something like this:

function recursiveResult(result, successfulResults, callback) {
  var item = result.shift();
  // if item is undefined, then we've hit the end of the array, so we'll call the original callback
  if (item !== undefined) {
    console.log(item, result);
    // do the POST in here, and in its callback, call recursiveResult(result, successfulResults, callback);
    successfulResults.push(item);
    return recursiveResult(result, successfulResults, callback);
  }
  // make sure callback is defined, otherwise, server will crash
  else if (callback) {
    return callback(successfulResults);
  }
  else {
    // log error... callback undefined
  }
}

function getItems(callback) {
  var successfulResults = [];
  var result = [1, 2, 3, 4];
  recursiveResult(result, successfulResults, callback);
}

console.log('starting');
getItems(function(finalResult) {
  console.log('done', finalResult);
});

Upvotes: 1

Related Questions