1

I'm using Express and trying to teach myself node/javascript callbacks and I've stumbled across something.

I have a route that looks like this:

var express = require('express');
var router = express.Router();
var api = require('../api');

router.get('/',function(req, res, next){
  var modulename = api.modulename;
  modulename.methodname(res);
});

module.exports = router;

And then the module that is being called above looks like this:

var library = require('library');
var instances = {};
var modulename = {

  getAllInstances: function(res) {
    var request = new library.asyncMethod();
    request.on('success', function(resp) {
      instances = resp.data; 
      res.setHeader("Content-Type","application/json");
      var returnInstances = {
        id: instances[0].InstanceId,
        state: {name: instances[0].State.Name, code: instances[0].State.Code}
      };
      res.send(returnInstances);
    })
    .on('error', function(resp){
      console.log(resp);
    })
  }
};

module.exports = modulename;

As you can see I'm passing through the response parameter through to my module, but I'd rather pass back instances and then in the route return api.modulename.instances, like this:

var library = require('library');
var instances = {};
var modulename = {

  getAllInstances: function() {
    var request = new library.asyncMethod();
    request.on('success', function(resp) {
      var returnData = resp.data;
      instances = {
        id: returnData[0].InstanceId,
        state: {name: returnData[0].State.Name, code: returnData[0].State.Code}
      };
    })
    .on('error', function(resp){
      console.log(resp);
    })
    .send();
  }
};

module.exports = modulename;

However, when I do, it's coming through as the default value {} but if I run it as above, I do get output so I know that there should be data in there.

2 Answers 2

1

Let me know if I have misunderstood your issue. If you are saying you want to pass back objects from getAllInstances then you pass in a callback and call it from the event handler like this-

router.get('/',function(req, res, next){
  var modulename = api.modulename;
  modulename.getAllInstances(res, function(err, instances){
     if(err){  ... }
     else{
        res.send(instances);    //or however you want to use instances
     }
  });
});

and in getInstances

var modulename = {
  getAllInstances: function(res, cb) {
    var request = new library.asyncMethod();
    request.on('success', function(resp) {
      instances = resp.data; 

      var returnInstances = {
        id: instances[0].InstanceId,
        state: {name: instances[0].State.Name, code: instances[0].State.Code}
      };
      cb(null, instances);
    })
    .on('error', function(err){
      cb(err, null));
    });
    //.send();    not sure what this is it seems to be request.send()  ??
  }
};
Sign up to request clarification or add additional context in comments.

4 Comments

Where would cb (the callback function) be declared?
The normal use with asyncronous programming in javascript is to declare it as an anonymous function. On the example above it is declared where it is used modulename.getAllInstances(res, function(err, instances){ (i'll put a comment in). It is a very common usage, particularly with node. If you find you use the same callback in many places you can declare a normal function and just pass the function name in as the cb parameter. Either way is fine.
Check out this article for a quick example en.wikibooks.org/wiki/JavaScript/Anonymous_Functions look to the bit where it says "The most common use for anonymous functions are as arguments to other functions"
As StevenTux says, you can use promises in place of callbacks. They are handy where you have a number of nested callbacks but I would get familiar with callbacks first and then look at promises when you feel you need them
0

The problem here lies with when the response from the API call is available. The event loop in Node means code won't block until the API replies with a response. Hence a callback is needed to handle that response when it becomes available. You probably want to use the API response in your Express router response so there's a chain of dependency.

One strategy here would be to use promises and not callbacks, it would alleviate some of the pain you're experiencing with async response from the API call.

In your routes:

router.get('/',function(req, res, next){
  var instances = [];
  // The function below could be refactored into a library to minimise controller code.
  var resolver = function (response) {
    var data = JSON.parse(response);
    instances.push({
      name: data[0].State.Name,
      code: data[0].State.Code
    });
    res.render('instances'. {instances : instances});
  };

  modulename.methodname(resolver);
});

And in your module:

var rp = require('request-promise'); // Also see q-io/http as an alternate lib.

var modulename = {
  methodname: function (resolver) {
    rp('http://the-inter.net')
      .then(resolver)
      .catch(console.error);
  }
};

This might not cut-n-paste work but have a look at the request-promise examples for further clarification.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.