I am trying to figure out how to create asynchronous functions for a web app. I am doing a database query, manipulating the data to a format that is more convenient, and then trying to set my router to pass back that file.
//Module 1
//Module 1 has 2 functions, both are necessary to properly format
function fnA(param1){
db.cypherQuery(query, function(err, result){
if(err){
return err;
}
var reformattedData = {};
//code that begins storing re-formatted data in reformattedData
//the function that handles the rest of the formatting
fnB(param1, param2);
});
});
function fnB(param1, reformattedData){
db.cypherQuery(query, function(err, result){
if(err){
return err;
}
//the rest of the reformatting that uses bits from the second query
return reformattedData;
});
});
exports.fnA = fnA;
Then in my router file:
var db = require('module1');
router.get('/url', function(req,res,next){
db.fnA(param1, function(err, result){
if (err){
return next(err);
}
res.send(result);
});
});
When I tried to test this out by hitting the URL indicated by the router, it just loads indefinitely.
I know what I have above is wrong, since I never wrote my function to require a callback. When I tried figuring out how to rewrite it though, I got really confused - How do I write my function to have a callback when the asynchronous stuff happens inside it?
Can someone help me rewrite my functions to use callbacks correctly, so that when I actually use the function, I can still work with the asynchronous response?
You use db.fa from your router file, and pass the second parameter as a callback function. but the function signature don't have the cb param and doesnt use it.
The main idea - you try to initiate an async operation and cannot know when it ends, so you send it a callback function to get triggered when all operations are done.
Fixed code should be like that:
//Module 1
//Module 1 has 2 functions, both are necessary to properly format
function fnA(param1, cb1){
db.cypherQuery(query, function(err, result){
if(err){
cb1(err); <-- return error to original call
}
var reformattedData = {};
//code that begins storing re-formatted data in reformattedData
//the function that handles the rest of the formatting
fnB(param1, param2, cb1);
});
});
function fnB(param1, reformattedData, cb1){
db.cypherQuery(query, function(err, result){
if(err){
cb1(err); <-- return error to original call
}
//the rest of the reformatting that uses bits from the second query
cb1(false, dataObjectToSendBack); <--- This will call the anonymouse function in your router call
});
});
exports.fnA = fnA;
Router file:
var db = require('module1');
router.get('/url', function(req,res,next){
db.fnA(param1, function(err, result){ <-- This anonymous function get triggered last
if (err){
return next(err);
}
res.send(result);
});
});
Related
I'm just starting to work with Javascript and Node, and Async and callbacks concepts are not something I have under control right now.
I have to call a function for each element of a documents Array. This function will call to DB and get me an array of the document annotations. I want to get all the annotations and put them on the same array. Something similar to this:
//function in an async waterfall
function(docs,callback){
let annotationsArray = [];
async.each(docs, (doc, callback2) => {
getAnnotationsFromDocument(doc.Id, callback2);
}, function (err,annotations){
if (err){
callback(err);
}
annotationsArray = annotationsArray.concat(annotations);
callback(null, annotationsArray);
});
},
//Next waterfall function
About the getAnnotationsFromDocument function, this is a simplified structure of it:
function getAnnotationsFromDocument(docId,callback){
initDB();
var async = require('async');
async.waterfall([
function authorize(callback){
//checkAuthorization
(...)
},
function getRfpdocAnnotations(auth, metadata, callback){
//call to DB
(...)
},
function processRfpdocAnnotations(rfpDocAnnotations,metadata,callback){
(...)
callback(null,annotationsList);
}
], function (err, result) {
if(err) {
callback(err);
} else {
callback(null, result);
}
});
}
Unfortunately, I'm unable to code it properly. I'm unable to get the results from the function before exiting the async.each. Could somebody explain me how to structurate the code for this?
Debugging I've found that the function getAnnotationsFromDocument gets the data and execute the last callback(null, result); properly, but when I get to function (err,annotations){, annotations is undefined.
Ok, I think I got it:
First problem was that async.each doesn't return the results on the callback like I was expecting. Unlike waterfall, it just returns the errors. I should have payed more attention reading the documentation.
Secondly, I had to create a callback on the getAnnotationsFromDocument call to process the results.
And finally, I was not executing the call to the callback of async.each, so the execution didn't get to the async.each callback and didn't continue to the next async.waterfall function.
To be quite honest, I'm not sure it's a correct answer, but it does what I was trying to achieve.
// function part of an async.waterfall
function(docs,callback){
let annotationsArray = [];
async.each(docs, (doc,callback2) => {
getAnnotationsFromDocument(doc._id, function(err,result){
if (err){
callback2(err);
}else{
annotationsArray = annotationsArray.concat(result);
}
callback2();
})
}, (err) =>{
if( err ) {
callback(err);
} else {
callback(null,annotationsArray); //to the next waterfall function
}
});
This question already has answers here:
How to return value from an asynchronous callback function? [duplicate]
(3 answers)
Closed 8 years ago.
I have this nodejs function, which is for inserting some data into db, and when it finishes inserting it should return 'success' or 'failure'.
This function is in the file insert.js
function insert (req,res) {
var d = req.body;
new dbModel({
name: d.dname,
desc: d.desc
}).save(false,function(err){
if (err){
return 'failure';
}else{
return 'success';
}
});
}
module.exports.insert = insert;
It inserts data into the db, but it doesnt return any value.
This is the route which invokes the above function and this is in the routes.js file.
router.post('/insert', function(req, res) {
var result = insert.insert(req,res);
res.render('insert',{title: 'insert',message: result});
});
Am I doing anything wrong or does this have something to do with async nature of nodejs.
Please help. Thanks.
EDIT
I tried as #Salehen Rahman said in the answers below, but the page isn't rendering and the browser keeps on waiting for reply from the server and i tried to log the value of result inside the callback function and again no output, it seems the code inside the callback function is not running. I checked the db and data has been inserted successfully.
That dbModel#save method is asynchronous, and so, the return keyword will only return to the inner the anonymous function. You want to use callbacks, instead. Also remove false from the save method. It can have only callback function as a parameter.
So, your insert function will look like this:
function insert (req, res, callback) {
var d = req.body;
new dbModel({
name: d.dname,
desc: d.desc
}).save(function(err){
if (err){
// Instead of return, you call the callback
callback(null, 'failure');
}else{
// Instead of return, you call the callback
callback(null, 'success');
}
});
}
module.exports.insert = insert;
And your route function will look like this:
router.post('/insert', function(req, res) {
insert.insert(req, res, function (err, result) {
res.render('insert',{title: 'insert', message: result});
});
});
Ideally, though, whenever an inner callback returns an error, you should also call your callback with the error, without any result; absent an error, and at the presence of a successful function call, you set the first parameter to null, and the second parameter to your result.
Typically, the first parameter of the callback call represents an error, where as the second parameter represents your result.
As a reference, you may want to read up on Node.js' callback pattern.
I'm ok with javascript and callbacks, but I'm getting really annoyed at this and need to call on the the world of stackoverflow for help.
I have written a function, to be used in the following way:
var meth = lib.funcTion(a,b); // meth should hold an array of properties { c, d } once executed
So now inside lib.js, we have a structure like:
exports.funcTion = function (a,b) {
database.connect(params, function(err,get){
get.query(querylang, function(err, results){
var varsIwantToReturn = { var1: results[i].foo, var2: results[i].bar };
});
});
// Now how do i return 'varsIwantToReturn'?
};
I have seen some things about incorporating callback() into the function, but I'm not exactly sure how this works. I've also seen some people use exec() - again, im not sure on how or why to use it.
Please help :) thanks in advance.
Well, it's all asynchronous so if you attempt to return it - it'll return undefined. In JavaScript (Sans the new yield keyword) functions execute from top to bottom synchronously. When you make an IO call like a database call - it still executes synchronously. In fact- when varIwantToReturn gets population the function has long run and terminated.
What is left is to do the same thing async functions like database.connect and get.query do and have the function take a callback:
exports.function = function (a,b, callback) {
database.connect(params, function(err,get){
if(err) return callback(err, null); // don't suppress errors
get.query(querylang, function(err, results){
if(err) return callback(err, null); // don't suppress errors
var varsIwantToReturn = { var1: results[i].foo, var2: results[i].bar };
callback(null, varsIwantToReturn);
});
});
};
Then you'd call it like
myExportedFunction(myA,myB, function(err, resp){
if(err) recoverFromError(err);
// varsIWantToReturn are contained in `resp`
});
I'm trying to use a callback to get the result of a query from a database, but I am still getting undefined returned to me. Here is the code:
function onComplete(x){
return x;
}
function getRecords(callBack){
mongo.MongoClient.connect("mongodb://localhost:27017/nettuts", function(err, db){
if(err){
console.log(err);
return;
}
console.log('Connected to mongo');
db.collection('nettuts').find().toArray(function(err, records){
if(typeof callBack === "function"){
callBack(records);
}
});
});
}
app.get('/', function (req, res) {
var records = getRecords(onComplete);
console.log(records);
res.render("index", {title: "Hello!", people: records});
});
On the third to last line, I am getting an undefined.
As stated, you are following this the wrong way around for asynchronous programming, if you want "re-use" then you are passing in the callback to the function you are re-using and not the other way around:
var mongo = require("mongodb"),
MongoClient = mongo.MongoClient;
function getRecords(db,callback) {
if (typeof callback !== 'function')
throw "getRecords() requires a callback as the second arg";
db.collection("test").find().toArray(function(err,records) {
callback(err,records);
});
}
MongoClient.connect('mongodb://localhost/test',function(err,db) {
getRecords(db,function(err,result) {
if (err) throw err;
console.log(result);
});
});
Also noting here that any of your active code needs to happen "after" the connection is present, too broad to cover here on the best way to do that. But you certainly do not establish a connection with every request, as that would be very bad.
You "re-usable" function then just accepts the connection details and the actual functionality you want to happen when the operation is complete. That is the basic principle of callbacks.
Why is resultofgetCauses undefined? I'm not sure if the function is not returning currentCauses or if it not being assigned to resultofgetCauses . . . or if this has something to do with asynchronicity.
function getCauses(){
var currentCauses;
client = pg.connect(connectionString, function(err, client, done){
if(err) console.log(err);
client.query('SELECT * FROM causes', function(err, result){
//console.log(result.rows);
console.log('poo');
currentCauses=result.rows;
//console.log(currentCauses);
});
});
return currentCauses;
};
var resultofgetCauses = getCauses();
Yes since it's running asynchronously, by the time result.rows is assigned to the currentCauses variable, the line 'return currentCauses' has already been executed thus the value is undefined.
You may want to do that as follow
var resulttofgetCauses;
function getCauses(){
var currentCauses;
client = pg.connect(connectionString, function(err, client, done){
if(err) console.log(err);
client.query('SELECT * FROM causes', function(err, result){
//console.log(result.rows);
console.log('poo');
currentCauses=result.rows;
//console.log(currentCauses);
resulttofgetCauses = currentCauses;
});
});
};
getCauses();
To be more specific with the answer, executing 'SELECT * FROM causes' sql does not give you the result right after its execution time. It takes at least 0.00something seconds to retrieve the data from database. So in the very short time between 'executing sql' and 'receiving requested data' JavaScript has already executed return currentCauses; while the correct data is still in the progress of retrieving. Because it's async and won't wait. There are good example code out there on the internet you may want to check out.
Plus It's a good practice to define function as follow
getCauses = function () { ... }
Because currentCauses is initialized in an async invoked function (the function passed as param to pg.connect) after you have already returned from getCauses function. By the time you return the value of currentCauses is undefined.