Javascript return is not returning anything - javascript

I have a NodeJS server and a helper function that is making a HTTP request, but when I make the call for the function, it comes up as undefined. The call is being made in a callback, so I don't think that there is a problem with the Async part of it.
This is the server.js
console.log(dictionary('wut'));
And this is the function dictionary.
if(dictionary[word]===undefined){
request({
url:"https://mashape-community-urban-dictionary.p.mashape.com/define?term="+word,
headers:{
'X-Mashape-Key':'KEY HERE',
'Accept':'text/plain'
}
}, function(err, response, body){
if(!err && response.statusCode==200){
var info = JSON.parse(body);
console.log(info.list[0].definition);
return info.list[0].definition;
}
});
} else {
return dictionary[word];
}
Where word is the word that is being passed to the function.
Edit: I forgot to mention that the dictionary function has
module.exports = function(word){
The return statements are supposed to give the module the value from the callback. Sorry about that, that's kind-of important information.

You're going to want to use a callback method with your helper method.
So your helper defintion would look like this:
function dictionary(word, callback) {
request({}, function(err, res, body) {
if (err) {
return callback(err);
}
callback(null, body);
});
}
And your call would become:
dictionary('wut', function(err, result) {
if (err) {
return console.error('Something went wrong!');
}
console.log(result);
});
This is obviously a very simple implementation, but the concept is there. Your helpers/ modules/ whatever should be written to accept callback methods that you can then use to bubble up errors and handle them in the appropriate place in your application. This is pretty much the standard way to do things in Node.
Here is how you could call your helper using a simple Express route:
router.route('/:term')
.get(function(req, res) {
dictionary(req.params.term, function(err, result) {
if (err) {
return res.status(404).send('Something went wrong!');
}
res.send(result);
});
});

From my viewpoint, it looks like that request library you're using was designed asynchronous. This means that you should handle the data inside of the callback function.
For instance:
function handle(data) {
// Handle data
console.log(data)
}
if(dictionary[word]===undefined){
request({
url:"https://mashape-community-urban-dictionary.p.mashape.com/define?term="+word,
headers:{
'X-Mashape-Key':'KEY HERE',
'Accept':'text/plain'
}
}, function(err, response, body){
if(!err && response.statusCode==200){
var info = JSON.parse(body);
console.log(info.list[0].definition);
handle(info.list[0].definition)
}
});
} else {
handle( dictionary[word] )
}
You didn't provide enough information for me to set this up correctly. But hopefully this gives you an idea on what you need to do.
To elaborate on why you should set it up this way:
The request function appears to be asynchronous, so keep it how it was designed.
You're returning inside of the callback, so your outer dictionary function isn't getting that returned data, the callback is.
Since the request function was designed async, there isn't a way to return the data to the dictionary without forcing it to be synchronous (which isn't advised). So you should restructure it to be handled inside of the callback.
(Another little note, you should use typeof dictionary[word] === "undefined", because I believe JavaScript sometimes throws an error otherwise.)

Related

Reading data off JSON file and saving it to database

I am trying to upload a JSON file to my express app, read data from the JSON file and save it as a new document in my MongoDB database.
The documents get saved but the fields don't get added, and that is due to the fields being undefined for some reason, How do I fix this?
When I console.log(obj) i can see the data but when I console.log(obj['DataSource_Name']) for example I get undefined why?
app.post("/upload-config", uploads.single("txt"), async (req, res) => {
// Read the file and send to the callback
fs.readFile(req.file.path, handleFile);
let obj;
// Write the callback function
function handleFile(err, data) {
try {
obj = JSON.parse(data);
console.log(obj["DataSource_Name"]);
} catch (err) {
console.log(err);
}
}
const config = new ConfigM({
DataSource_Name: obj.DataSource_Name,
DataSource_ID: obj.DataSource_ID,
DataSource_Type: obj.DataSource_Type,
DataSource_ICON: obj.DataSource_ICON,
DoubleQuotes_Text: obj.DoubleQuotes_Text,
Delimeter: obj.Delimeter,
Field_list: obj.Field_list,
});
try {
await config.save();
res.send(obj);
} catch (err) {
console.error(err);
}
});
obj is only going to be defined when fs.readFile is ready which is NOT as soon as you called it. That happens asyncronously. So after fs.readFile() is called, the code just continues executing, creating the config object and obj is still not defined.
If you would like to use callback functions as you have here, you probably want your config creation, config.save() and res.send(obj) to be part of the readFile callback. Maybe to try before refactoring you could simply move the closing } right after the } of your final catch.
app.post("/upload-config", uploads.single("txt"), async (req, res) => {
// Read the file and send to the callback
fs.readFile(req.file.path, handleFile);
let obj;
// Write the callback function
function handleFile(err, data) {
try {
obj = JSON.parse(data);
console.log(obj["DataSource_Name"]);
} catch (err) {
console.log(err);
}
const config = new ConfigM({
DataSource_Name: obj.DataSource_Name,
DataSource_ID: obj.DataSource_ID,
DataSource_Type: obj.DataSource_Type,
DataSource_ICON: obj.DataSource_ICON,
DoubleQuotes_Text: obj.DoubleQuotes_Text,
Delimeter: obj.Delimeter,
Field_list: obj.Field_list,
});
try {
await config.save();
res.send(obj);
} catch (err) {
console.error(err);
}
}
});
I am not certain what you precisely are doing by reading the file locally while you got it from the post. Be aware that you will want a lot more security guards all over the place and not accepting path names from the request to decide the path name on your server. But I guess that's all a topic for another stackoverflow question :)

node.js : How do I pass in multiple variables into a view?

I'm trying to create a webpage where there is an instance of all the current Projects I am working on, on the left, so I'd need a .forEach() function in order to loop through all of them in order to display it, but on the other side, I need to display the information that is currently selected.
Please first take a look at my code block so I can try to explain the thought process behind what I was trying to do.
So I didn't have any problems selecting the information of the single project that I needed to display on this webpage. I used the .findOne() function in order to pick out the information that I needed.
The problem that I'm facing is that I also need to pass a var that's connected to the .find() function in order to pass through all of the elements of the database. The way I went about this is that I thought I would be able to set the definition of allProjects by manually running the .find() function, and then returning it, thus assigning Projects.find() to allProjects.
app.get('/projects/:url', (req, res) => {
Projects.findOne({ Url: req.params.url }, (err, foundProject) => {
if (err) {
console.log(err);
} else {
res.render('show', {
foundProject: foundProject,
allProjects: Projects.find({}, (err, allProjects) => {
if (err) {
res.send('error');
} else {
return allProjects;
}
})
});
}
});
});
I thought that by returning allProjects and then also having that assigned to allProjects, i'd be able to use the allProjects variable in my show.ejs page.
Unfortunately, I'm getting an error 'allProjects.forEach() is undefined' which leads me to believe that in the app.js where I am defining allProjects, it's not being assigned the correct value that I want it assigned.
It looks like you're expecting return allProjects to do something, but that's actually ignored. Unless you have a callback function you can call, that will go into the void and never be seen by anyone. This is true of virtually all callback functions. They do not care what value that function returns because it's never relevant, what they want is the future value which comes through the callback given to this function.
In other words it plays out like this:
asyncFunctionTakingCallback(function(cb) {
cb(null, value); // This is the important value!
return value; // Nobody cares about this value. Don't even bother.
});
To fix that you need to move the render call inside of the inner-most callback function:
app.get('/projects/:url', (req, res) => {
Projects.findOne({ Url: req.params.url }, (err, foundProject) => {
if (err) {
console.log(err);
// Return here to avoid another level of indentation below
return;
}
Projects.find({}, (err, allProjects) => {
if (err) {
res.send('error');
} else {
res.render('show', {
foundProject: foundProject,
allProjects:
});
}
});
});
});
Now that's still a dizzying amount of code and the nesting here is getting completely out of control even though this is relatively simple Node code.
For comparison here's a version that uses async functions:
app.get('/projects/:url', async (req, res) => {
let foundProject = await Projects.findOne({ Url: req.params.url });
res.render('show', {
foundProject: foundProject,
allProjects: await Projects.find({})
});
});
There's really not much to it this way. What await does is basically stall out on that line and wait for the promise to get resolved or produce an error. Any errors produced should be captured with try { ... } catch as usual.

How to do things synchronously in node

I know node is all about async stuff but I want to do things in serial mode as follows:
make api request > convert body xml to JSON.stringify > pass string to template.
request.get({url:url, oauth:oauth}, function(err, res, body){
parseString(body, function(err, result){
output = JSON.stringify(result);
res.render('home', { title: 'Fantasy Home',
output: output });
});
});
Now I want to do this in sequence, but with all the callbacks I'm confused.
res.render doesn't work nested inside callbacks because the res object doesn't exist. Having it outside won't work because it'll run before the callbacks execute so you'd get "undefined" for output.
There has to be a way to do things in sequential order. Why is everything a callback?? Why can't these functions just return a regular non-callback result?
How can I make this work?
The others fail to mention why your res.render does not work.
You probably have something like this:
app.get('/', function(req, res, next) { // You need the res here to .render
request.get({url:url, oauth:oauth}, function(err, res, body){ // But you are using this res instead, which does not have the render method
parseString(body, function(err, result){
output = JSON.stringify(result);
res.render('home', { title: 'Fantasy Home',
output: output });
});
});
});
Read the comments in the code. So your solution is, use res.render from the request handler, rename res in the request.get callback to something else.
You should use middlewares, also promises is better thing to work with async in node, but I'll show you with callbacks. It is strongly suggested to not to block your thread with synchronous calls! Since node.js is single threaded. next() is an callback here so middleware won't allow execution of main route function (with res.render) until next() is called. You can pass as many middlewares as you wish.
app.use('/user/:id', middleware, (req, res) => {
//here you'll find your data
console.log(req.callData);
res.render(view, req.callData);
}
middleware(req, res, next) => {
dotheCall(dataToPass, (err, cb) => {
req.callData = cb;
// remember about handling errors of course!
return next();
})
}
JavaScript is single threaded if we use synchronous code then that will itself a big problem around response time (node.js) and all. Everything is implemented with callback fashion due to the benefit of event loop.
You can take a deep understanding of event loop : https://youtu.be/8aGhZQkoFbQ (Very good explaination)
You can use Promisification for the scenario you want to implement : http://bluebirdjs.com/docs/getting-started.html
request.get({url:url, oauth:oauth}, function(err, res, body) {
// Note :: This is Promisified Function
return parseString(body)
.bind(this)
.then(function(result) {
output = JSON.stringify(result);
res.render('home', {title: 'Fantasy Home', output: output });
return true;
})
.catch(function(error)
{
// Error Handling Code Here
// Then send response to client
});
});
You can implement promisified function using the following approach
function parseString(body) {
var Promise = require("bluebird");
return new Promise(function(resolve,reject) {
// Your Parsing Logic here
if(is_parsed_successfully) {
return resolve(parsed_data);
}
return reject(proper_err_data);
})
}

setTimeout inside a callback failed to remove a mongodb document [duplicate]

I am using stripe for payments in my app, I want to create a receipt document in my own database after a succesful transaction
My code:
Meteor.methods({
makePurchase: function(tabId, token) {
check(tabId, String);
tab = Tabs.findOne(tabId);
Stripe.charges.create({
amount: tab.price,
currency: "USD",
card: token.id
}, function (error, result) {
console.log(result);
if (error) {
console.log('makePurchaseError: ' + error);
return error;
}
Purchases.insert({
sellerId: tab.userId,
tabId: tab._id,
price: tab.price
}, function(error, result) {
if (error) {
console.log('InsertionError: ' + error);
return error;
}
});
});
}
});
However this code returns an error:
Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.
I am not familiar with Fibers, any idea as to why this is?
The problem here is that the callback function which you pass to Stripe.charges.create is called asynchronously (of course), so it's happening outside the current Meteor's Fiber.
One way to fix that is to create your own Fiber, but the easiest thing you can do is to wrap the callback with Meteor.bindEnvironment, so basically
Stripe.charges.create({
// ...
}, Meteor.bindEnvironment(function (error, result) {
// ...
}));
Edit
As suggested in the other answer, another and probably better pattern to follow here is using Meteor.wrapAsync helper method (see docs), which basically allows you to turn any asynchronous method into a function that is fiber aware and can be used synchronously.
In your specific case an equivalent solution would be to write:
let result;
try {
result = Meteor.wrapAsync(Stripe.charges.create, Stripe.charges)({ /* ... */ });
} catch(error) {
// ...
}
Please note the second argument passed to Meteor.wrapAsync. It is there to make sure that the original Stripe.charges.create will receive the proper this context, just in case it's needed.
You might want to take a look at the docs for http://docs.meteor.com/#/full/meteor_wrapasync.

How can I structure these callbacks to get me the info I want?

Maybe I've been staring at my code too long or maybe I'm using too many callbacks elsewhere or something, but I can't figure out the logistics of this one. I know the value contained in rows is correct from using util.inspect(rows) on it inside that callback function. I just don't know how to bring it back up to the surface, so to speak. dbHandler() is called from other functions that pass the params and a result is expected in return.
Any insight would be tremendously helpful.
Using node.js with Express and mysql packages if that makes a difference.
function dbHandler(req, res, params) {
pool_user.getConnection(function (err, connection) {
if (err) {
connection.release();
res.json({
"code" : 100,
"status" : "Error in connection database"
});
return;
}
connection.query(params.query_string, function (err, rows) {
connection.release();
if(!err) {
res.json(rows); // <<--- I want to get ahold of the value of `rows`
//console.log(util.inspect(rows));
}
});
connection.on('error', function(err) {
res.json({
"code" : 100,
"status" : "Error in connection database"
});
return;
});
});
}
Trying to return the result of an async function is an anti-pattern and will never work.
Instead, restructure the calling code.
Not Like This - the anonymous callback does not return to the caller
new_data = api_request_data(auth, request,
function(error, data){
return data;
});
store(new_data);
update_screen(new_data);
Better
api_request_data(auth, request,
function(error, data){
store(data);
update_screen(data);
});
Best
api_request_data(auth, request,
function(error, data){
if (error){
console.log(error);
return;
}
store(data);
update_screen(data);
});
In your code, the data pipeline looks ok.
connection.query(params.query_string, function (err, rows) {
connection.release();
if(!err) {
res.json(rows); // <<--- I want to get ahold of the value of `rows`
//console.log(util.inspect(rows));
}
});
The code res.json(rows); in a node.js application will queue a JSON string containing the data in rows for writing to the current web connection referenced in res.
Your question suggests that the issue is that this line does not return data to the caller of dbHandler(), but no trained node.js developer would expect that to happen. The code looks correct for delivering the data in rows to an over-the-web client of the node.js application.

Categories