8

I am currently having an issue with figuring out how to wait for the request to finish before returning any data. I do not believe I can do this with a Callback and I have not been able to figure out a good way of using the EventEmitter to do it. The reason I cannot use a callback is because my flow currently works like this.

Request comes into server > Generate XML > Contact remote API for details to finish generating XML > Finish Generating XML > Return request to client

The code I currently have looks very similar to the code included below.

Web Server:

var xml = require('./XMLGenerator');
response.writeHead(200, {'Content-Type': 'text/xml'});
response.write(xml.generateXML());
response.end();

XML Generator:

function generateXML(){
// Code to generate XML
var API = require('./API');
var response = API.getItems("5");
for(var i = 1; i <= response.length; i++)
{
  // more code to generate further XML using the API response
}

// Finish generating and return the XML
}

API Grabber:

function getItems(sort_by, amount) {
var request = require("request")

var url = "https://url.com/api/get_items.json?amount=" + amount;
request({
    url: url,
    json: true
}, function (error, response, body) {
    console.log('we got here!');
   if (!error && response.statusCode === 200) {
        var items = body.data.items;
        console.log(items);

        return items;
    } else {
        console.log("Error connecting to the API: " + url);
        return;
    }
})
}

When running the code and testing directly it returns "undefined" meaning that the request has not been made yet. I just need to know a way to make the XML generator wait for the request to finish before continuing on with the generation. (there may be minor errors in the psudeo code I typed up as it is not an exact copy paste from the source, it does however work in this flow)

Am I just using bad practices, or is this the correct way that I should be attempting this?

EDIT: The problem is not loading the module/API file, that loads perfectly fine. The problem is that the request takes about 2 seconds to complete, and that node moves on before the request completes.

OstlerDev
  • 513
  • 2
  • 6
  • 11
  • i dont get why are not you using the callback ? – ProllyGeek May 22 '15 at 06:31
  • possible duplicate of [Node.js - asynchronous module loading](http://stackoverflow.com/questions/20315434/node-js-asynchronous-module-loading) – ProllyGeek May 22 '15 at 06:33
  • @ProllyGeek I cannot use the callback because the way that the flow works, the web server is attempting to serve the content immediately. Unless there is a way that I can serve content at a later time then after the immediate request then I have to wait. – OstlerDev May 22 '15 at 06:33
  • please check the duplicate question. – ProllyGeek May 22 '15 at 06:34
  • The problem is not loading the module/API file, that loads perfectly fine. The problem is that the request takes about 2 seconds to complete, and that node moves on before the request completes. – OstlerDev May 22 '15 at 06:41
  • What are you using as your web server? Node http.createServer? Or are you doing CGI. Either way, you've misunderstood your server code. It does not attempt to serve the content immediately. It only serves content when you call `response.write()` until then it waits. So you can call `.write()` and `.end()` in a callback. – slebetman May 22 '15 at 06:46
  • @OstlerDev yes it is pretty clear , which you should handle yourself , everthing should go on normally till the request completes , but data handling depending on request sould be handled in the call back , i can be more helpful , if you specify what data is it , and how are you planning to handle it . – ProllyGeek May 22 '15 at 06:46

2 Answers2

8

You need to use callbacks. Change your API grabber to this:

function getItems(amount, callback) {
// some code... 
request({
  url: url,
  json: true
}, function (error, response, body) {
   // some code...
   if (!error && response.statusCode === 200) {
      // some code    
      callback(items); <-- pass items to the callback to "return" it
   }
})
}

Then change the xml generator to also accept callbacks:

function generateXML(callback){
// Code to generate XML
var API = require('./API');
API.getItems("5",function(response){
  for(var i = 1; i <= response.length; i++)
  {
    // more code to generate further XML using the API response
  }

  // Finish generating and return the XML
  callback(xml_result); // <-- again, "return" the result via callback
});
}

Then in your server code do:

var xml = require('./XMLGenerator');
response.writeHead(200, {'Content-Type': 'text/xml'});
xml.generateXML(function(xmlstring){
    response.write(xmlstring);
    response.end();
});
slebetman
  • 109,858
  • 19
  • 140
  • 171
3

Do these changes since request is async, use callbacks.

API.getItems("5", function(rs){
      var response = rs;
      for(var i = 1; i <= response.length; i++)
      {
      // more code to generate further XML using the API response
      }

      // Finish generating and return the XML
    }

});


...
function getItems(sort_by, amount, callback) {...

...
callback(items); //Instead of return items;

...

You cannot return from an async call, which is request module in your case. In such cases you can either use promises or callbacks. This is a solution with callback.

The actual problem of it returning undefined is it doesnot wait for var response = API.getItems("5"); to execute completely and executes the next line and hence you get response as undefined. I hope you get the point.\

Also I hope

response.writeHead(200, {'Content-Type': 'text/xml'});
response.write(xml.generateXML());
response.end();

is somewhere inside some callback of an API or http.createServer.

Zee
  • 8,420
  • 5
  • 36
  • 58
  • Could you go into a little more depth? Your explanation is very confusing and sadly does not make sense to me as to how it would help. – OstlerDev May 22 '15 at 06:42
  • I was mistaken as to how exactly callbacks work. Thank you for clarifying, and yes, that is just a small snippit of the web server code, I am using http.createserver. – OstlerDev May 22 '15 at 06:56
  • @OstlerDev. change `generateXML()` to have a callback too. `// Finish generating and return the XML` should also be a callback. – Zee May 22 '15 at 07:00
  • 1
    Thanks for your help @Zee, there was another answer that also explained just that. It now works perfectly fine and I understand how callbacks work a bit more as well. Thank you for taking pity on me :) – OstlerDev May 22 '15 at 07:06