This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 8 years ago.
I'm new to Node and Express, and am running into trouble getting data from one function, to pass into another function as a var. I presume that this is because I am not handling the callback and async stuff properly, but would appreciate a steer.
The code should hopefully be explanatory. I am attempting to retrieve some JSON data from a URL, and pass it to the router.get function's render() method. However, nothing ("undefined") gets sent instead. When I run the getData() function separately, however, it returns the correct data, so as stated, I presume this is because I'm not handling the async stuff correctly.
function getData(cid){
var request = require("request");
var cid ='XXXXXX' // hard code temp
var baseUrl = "someurl.com"
var apiKey = "XXXXXX"
var cUrl = baseUrl+cid+'?api_key='+apiKey
request({
url: cUrl,
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
console.log(body._embedded.assets); //Print the json response
return body._embedded.assets
}
});
}
/* GET home page. */
router.get('/', function(req, res) {
var LiveCards = getData()
var cID = (req.query.cID
? req.query.cID
: '14d115ff-1db7-4a6f-8648-ea64bc1a6597')
var limit = Number(req.query.limit)
res.render('index', {
title: 'Cards',
cards: LiveCards.slice(0,limit), <--- returning undefined at the moment
limit: limit,
activateSharetools: activateSharetools,
cID: cID,
cardsHeader: req.query.cardsHeader,
});
});
Any guidance appreciated.
request is asynchronous function, you need add callback to get Data and execute it after completes the request, like this:
function getData (cid, callback) {
var request = require("request");
var cid = 'XXXXXX';
var baseUrl = "someurl.com";
var apiKey = "XXXXXX";
var cUrl = baseUrl + cid + '?api_key=' + apiKey
request({url: cUrl, json: true}, callback);
}
router.get('/', function (req, res) {
var cID = req.query.cID ? req.query.cID : '14d115ff-1db7-4a6f-8648-ea64bc1a6597');
var limit = Number(req.query.limit);
getData(cID, function (error, response, body) {
if (!error && response.statusCode === 200) {
res.render('index', {
title: 'Cards',
cards: body._embedded.assets.slice(0, limit), <--- returning undefined at the moment
limit: limit,
activateSharetools: activateSharetools,
cID: cID,
cardsHeader: req.query.cardsHeader,
});
} else {
throw error;
}
});
});
Related
I am building a WebApp that includes heavy AJAX calling from the frontend and NodeJS Express at the backend. My Frontend Code looks like this-
This is the global AJAX function I am using in my all projects-
function _ajax(params = {}, next = null, url = '', method = 'post', carry = null) {
params = this._isObjectValid(params, 1, -1) ? params : {};
for (let key in params) {
if (params.hasOwnProperty(key)) {
const checkObject = params[key];
if (typeof checkObject === 'object' || Array.isArray(checkObject)) {
params[key] = JSON.stringify(checkObject);
}
}
}
const httpRequest = new XMLHttpRequest();
httpRequest.open(method, url, true);
httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
const p = Object.keys(params).map(function (value) {
return encodeURIComponent(value) + '=' + encodeURIComponent(params[value]);
}).join('&');
if (next) {
httpRequest.onreadystatechange = function () {
next(httpRequest, carry);
};
}
httpRequest.send(p);
}
This is the global Click Event Binding function
function _click(next, target, node, extra = null) {
node.onclick = (event) => {
next(event, target, extra);
};
return node;
},
This is my AJAX Request
_click(
() => {
_ajax(
{mod: 'login'},
(request) => {
console.info(request.status);
console.info(request.response);
},
'http://localhost:1008/'
)
}, null, buttonSubmit);
My Backend Code for Handling Post AJAX Requests is:
app.post('*', async (req, res) => {
console.info('POST RECEIVED');
const params = req.body;
console.info(params);
await posts(req, res, params, dbo);
});
export async function posts(req, res, params, dbo) {
if (res) {
const mod = params.mod;
switch (mod) {
case 'user':
await sendPass(res, 'User Will', null);
break;
default:
await send(res, 'Invalid Module Call', null);
}
}
}
export function send(res, message, data, result = false) {
res.send({result: result, message: message, data: data});
res.end();
}
export function sendError(res, message, data) {
send(res, message, data, false);
}
export function sendPass(res, message, data) {
send(res, message, data, true);
}
Now in any other server like PHP or .NET, my web app is getting exactly one response from the server when I click the button, but for NodeJS I am getting three responses while I am processing the AJAX request only once-
This is repeating for every AJAX Request. So If I'm processing 3 AJAX Requests, my Web App is receiving 9 responses. I tried to search on the internet on this but can't find much. Since this scenario is not repeating on any other Server except NodeJs, so it may be not a problem from JavaScript event binding or ajax processing, or a browser issue.
Any suggestion will be appreciable. Thanks in advance.
I found the bug in the global AJAX function. I need to detect the status of readyState of httpRequest. I'm posting an updated version of the function:
Before:
httpRequest.onreadystatechange = function () {
next(httpRequest, carry);
};
After:
/**
* readyState values
* 0: request not initialized
* 1: server connection established
* 2: request received
* 3: processing request
* 4: request finished and response is ready
*/
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState === 4)
next(httpRequest, carry);
};
Surprisingly, it was not creating problems in other servers like WAMP or .NET for reasons I still don't know.
So, I have this POST request I made
$("#pacotes").on('click', ".produto", function () {
console.log(this.id);
$.post("http://localhost:3000/pacote?idPacote=" + this.id);
});
The log returns a number on the client side, as it should.
The post then goes through my route and arrives here
exports.Pacote = function (req, res) {
console.log("gato");
var pacote = req.idPacote;
console.log(pacote);
connection.connection();
global.connection.query('SELECT * FROM Pacote WHERE idPacotes = ? LIMIT 1', [pacote], function (err, result) {
if (result.length > 0) {
if (result) {
var object = JSON.parse(JSON.stringify(result));
var packObject = object[0];
if (result.length > 0) {
if (result) {
res.render('home', { title: 'pacote', layout: 'pacote', data: packObject });
}
}
} else if (err) {
console.log(err);
}
};
});
}
The first log is just a flag to see if it is reaching the point, which it is
But the second log should return a number, yet it is returning undefined
I'm not very experienced in this subject, but this has always worked for me.
I don't understand where I went differently as my login function is nearly the same thing and returns actual values as expected. Maybe because of bodyparser, but I dont know.
It just bothers me that the id returns properly on the client side but as undefined on the server side
I also tried the same thing but with GET and the results didnt change
You are passing "idPacote" in query string. You will get the the query string parameters in "req.query" if you are using Express with NodeJS. Try this
var pacote = req.query.idPacote;
instead of
var pacote = req.idPacote;
The var pacote = req.idPacote; should be replaced with (provided that you send it as GET parameter):
var pacote = req.params.idPacote;
A side note: you should be using connection pooling in order to improve performance in your app, for example:
var mysql = require("mysql");
//Database connection parameters
var config = {
connectionLimit: 10000,
host: "127.0.0.1",
user: "user",
password: "password",
database: "database",
charset: "utf8_general_ci",
connectTimeout: 4000
};
//Pool
var pool = mysql.createPool(config);
function connection(){
//Assign connection pool for further reuse
this.init = function () {
this.pool = pool;
};
//Get connection
this.acquire = function(callback){
this.pool.getConnection(function(error, con){
if (error) {
if (this.pool)
//Close all connections in pool
this.pool.end(function(err){});
console.log("\x1b[31m" + error, "\x1b[0m");
}
else {
callback(error, con);
}
});
};
}
Read more here.
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.
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() ??
}
};
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.
İ'm using Node.js , ExpressJS(5.0.0-alpha.1) and request module
This is POST request
app.post('/generateTags', function(req, res) {
var title = req.body.title;
restApiKeywords(title, function(tags) {
console.log(tags);
});
});
});
My callback function;
function restApiKeywords(postTitle,callback){
request_('blabla'+postTitle,{json:true,encoding: "binary"}, function (error, response, body) {
if (!error && response.statusCode == 200) {
var data = body[1];
if (typeof(callback) === 'function') {callback(data);}
}
});
}
if i call this function in GET method, it is working;
app.get("/test",function(req, res) {
restApiKeywords("Recep Niyaz", function(tags) {
console.log(tags);
});
});
How can i run this fuction into POST request basically ?
FİXED:it is my stupid issue , i created two same post method on same project , sorry my friends :)
For a personal project I'm trying to create a simple oEmbed system using Nodejs.
My route looks like this:
app.get('/oembed/:url', function (req, res) {
oembed.get(req.params.url, function (error, result) {
return res.json(200, {message: "OK"});
});
});
and oembed is exposed using var oembed = require('../oembed');
For the oembed.js itself I have:
var request = require('request');
exports.get = function(url, callback) {
//this bit will be developed to call different functions depending on the URL
html = vimeo(url)
};
function vimeo(url) {
var videoUrl = url;
var endpoint = 'http://www.vimeo.com/api/oembed.json';
var url = endpoint + '?url=' + encodeURIComponent(videoUrl) + '&width=640';
request(url, function (error, response, body) {
if (!error && response.statusCode == 200) {
var video = JSON.parse(body);
return video.html
}
})
}
So far, the vimeo function returns the desired html to the function call but I'm a bit lost from here. I'm not sure how I return the html to the initial request (the oembed.get part) and utilise the callback's error and result parameters.
Any help (and advice) would be much appreciated.
It seems you just don't understand how callback functions work. You need something like this in your oembed.js file:
var request = require('request');
exports.get = function(url, callback) {
var endpoint = 'http://www.vimeo.com/api/oembed.json';
request(endpoint + '?url=' + encodeURIComponent(url) + '&width=640', function (error, response, body) {
if (!error && response.statusCode == 200) {
try {
callback(null, JSON.parse(body).html);
} catch (error) {
callback(error);
}
}
});
};
And your route should look like this:
app.get('/oembed/:url', function (req, res) {
oembed.get(req.params.url, function (error, result) {
if (error) {
res.json(500, {message: error});
} else {
res.json(200, {message: "OK"});
}
});
});