PseudoAj PseudoAj - 3 years ago 236
Node.js Question

Node js lost in asynchronous behaviour: undefined

Objective




Disclaimer: I am new to node world and having tough time wrapping head around node asynchronous behaviour.


I am trying to write a wrapper function to do a
https.get
on a given url and return
json
output.

Code



const https = require('https');

// Get the user details
var myUrl = <valid-url>;

const getJson = function(url) {
// https get request
const req = https.get(url, (res) => {
// get the status code
const { statusCode } = res;
const contentType = res.headers['content-type'];

// check for the errors
let error;
if (statusCode !== 200) {
error = new Error('Request Failed.\n' +
`Status Code: ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
error = new Error('Invalid content-type.\n' +
`Expected application/json but received ${contentType}`);
}
if (error) {
console.error(error.message);
// consume response data to free up memory
res.resume();
return;
}

//parse json
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
console.log(parsedData);
} catch (e) {
console.error(e.message);
}
});
}).on('error', (e) => {
console.error(`Got error: ${e.message}`);
});
}

console.log(getJson(myUrl));


Output



undefined
{ user_id: <user-id>,
name: 'Ajay Krishna Teja',
email: <my-email> }


Issue



So the
https.get
is able to hit end point and get data but not able to return the json. Constantly returning
Undefined
.

Things I tried




  1. Returning
    parsedData
    on
    res.on(end)
    block

  2. Defining a
    var
    and copying
    parsedData

  3. Copying to a global variable (although I knew it's very bad practice)



Places I looked up




  1. Node.js variable declaration and scope

  2. How to get data out of a Node.js http get request

  3. Javascript function returning undefined value in node js



Updated: Working code



const getJson = function(url,callback) {
// https get request
const req = https.get(url, (res) => {
// get the status code
const { statusCode } = res;
const contentType = res.headers['content-type'];

// check for the errors
let error;
if (statusCode !== 200) {
error = new Error('Request Failed.\n' +
`Status Code: ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
error = new Error('Invalid content-type.\n' +
`Expected application/json but received ${contentType}`);
}
if (error) {
console.error(error.message);
// consume response data to free up memory
res.resume();
return;
}

//parse json
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
callback(parsedData);
} catch (e) {
callback(false);
console.error(e.message);
}
});
}).on('error', (e) => {
console.error(`Got error: ${e.message}`);
});

return req;
}

// calling
getJson(amznProfileURL,(res) => {
console.log(res);
});

Answer Source

Short answer: You are not returning anything in your getJson function and undefined is the default Node/Javascript return value.

function getJson(){
  callAsyncFunction(param1, param2, param3)
  // there is no return value!
}

Longer answer: Javascript (and Node as a result) is a single threaded language that uses callbacks as it's mechanism to return async results back to the callee. To do this, you pass a function into asynchronous functions as a parameter and then that function gets called at some point in the future whenever the asynchronous function is ready to send back it's result. Calling return from this "anonymous function" is actually just returning from the "callback" function you are sending into the async function.

function getJson(){
  console.log('A')
  // request is started, but getJson continues execution!
  http.get(url, (res)=> {
    console.log('C') // by the time I'm called, 'B' has already been printed and the function has returned!
    return true // this won't return getJson! It will only return the callback function which doesn't do anything!
  })
  console.log('B')
  // end of function without return value, return undefined!
}

// Will print 'A', 'B', 'C'

There are a couple different ways you can handle this. Callbacks have been used traditionally but Javascript also natively supports Promises which are a little easier to manage and are used in many popular frameworks by default.

You can implement your function with callbacks by providing your own callback parameter to call as soon as http.get returns itself.

// define getJson with second callback parameter
const getJson = function(url, callback) {
  http.get(url, (res) => {
    if(res){
      callback(res) // result came back, send to your own callback function
    } else {
      callback(false) // request failed, send back false to signify failure
    }
  })
}

// now I can use getJson and get the result!
getJson('http://getjson.com', (res) => {
 console.log('got result!', res)
})
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download