I have a javascript function "data.getOrdersByUsersModifiedDate" which is making a call to another function "database.getDb". The first function gets a "request" parameter object which has parameters defined. However I can't seem to be getting my head around as to how I can pass that parameter from data.getOrdersByUsersModifiedDate TO database.getDb so that I can use it at line
var orders = db.order.find().toArray(function(err, result)
How can I pass my request parameter from the top function "getOrdersByUsersModifiedDate" to lower function "database.getDb" so I can use it as a filter when I get orders
data.getOrdersByUsersModifiedDate = function (request, next) {
database.getDb(function(err, db) {
if(err) {
next(err);
} else {
var orders = db.order.find().toArray(function(err, result) {
if(err) return next(err);
next(null, result);
});
}
});
};
Use bind.
bind and apply allows a developer to specify the context of the function call (this).
Bind creates a new function with the arguments given to bind (from second parameter), whereas apply calls the function with the new context and the parameters passed as an array (see doc).
This will help you solve scoping hell in callback hell.
The poor man's way to achieve this is using let that=this.
Edit: As pointed by the OP in the comments, arrow functions in ES6 do not bind this ; thus using an arrow function solves the OP issue.
data.getOrdersByUsersModifiedDate = function (request, next) {
database.getDb(function(err, db) {
if(err) {
next(err);
} else {
let {arg1, arg2, arg3} = request; // This should work with ES6
var arg1 = request.arg1; // Otherwise this?
var orders = db.order.find().toArray(function(err, result) {
if(err) return next(err);
next(null, result);
});
}
}.bind(this)); // Scope this to the context of getOrdersByUsersModifiedDate
};
Related
I'm receiving a "TypeError: callback is not a function" which leads me to believe I am misunderstanding how to use callbacks in a nested function.
Function:
function getAudioInfo(filePath, callback) {
ffprobe(filePath, { path: ffprobeStatic.path }, function (err, info) {
console.log("ffprobe output: " + JSON.stringify(info));
if (err) {
console.log("getAudioInfo error: " + err);
callback(err, null);
} else {
callback(null, info);
}
});
}
Call:
function checkAudioInfo(metadata_json, callback) {
var filePath = metadata_json['current_path'];
getAudioInfo(filePath, function(err, info) {
if (err) {
callback(err);
}
//operations on info
callback(null, metadata_json);
});//end getAudioInfo
}//end checkAudioInfo
Is this an improper use of callbacks?
Edit:
Error was found in passing to the function wrapping checkAudioInfo (another callback error). I will make edits and post the correct code shortly. All your questions helped me figure out the answer. Thanks!
Your first block of code accepts a callback. The second argument should be a function. This function will be called when the asynchronous code is done.
Your second block of code calls the function in the first. The second argument you are passing is a function. So far, so good.
Inside that function, you try to call callback. This fails because there is no variable with that name in scope.
At this point, it is very unclear what you are trying to do. The function you are passing is the callback. You are supposed to use it to do something useful with the data it is passed by the code from the first code block.
Now, you could get a reference to the callback function by using a named function expression:
getAudioInfo(filePath, function callback (err, info) {
if (err) {
callback(err);
}
//operations on info
callback(null, metadata_json);
});//end getAudioInfo
… but then you are just calling it recursively and infinitely, which is not useful.
re Edit:
You have now added a second variable called callback:
function checkAudioInfo(metadata_json, callback) {
This makes more sense.
If that callback is undefined, then that is because you aren't passing it a value when you call checkAudioInfo. You haven't included that code.
The function itself function(err, info) is the callback which is called inside of getAudioInfo!
What is metadata_json? Do you mean info?
I dont know what is the problem with my code.
// emitter.js
var EventEmitter = require('events').EventEmitter;
var util = require('util');
function Loadfun(param1, param2, db){
function __error(error, row){
if(error){
this.emit('error', error);
return true;
}
if(row.length < 1)
this.emit('failure');
}
function doSomething(){
db.query('select something', callback);
}
function callback(err, result){
if(__error(error))
return false;
else
this.emit('success', result);
}
this.doSomething = doSomething;
};
util.inherits(Loadfun,EventEmitter);
module.exports = Loadfun;
This is the emitter function.
and i am using this for some sync db works.
the following is the calling function.
var emitter = require('emitter');
router('/fetch', function(req, res){
var fetch = new emitter(param1, param2, db);
fetch.on('failure', function(){
console.log('error');
});
fetch.on('success', function(data){
console.log(JSON.stringify(data));
});
fetch.doSomething();
});
this works perfectly fine without any errors.
I tried logging the flow till the emiting of success
but the catching of the event emitting is not getting logged..
I dont understand what is the problem.. It would be nice if someone could help.
2 things that I can quickly see are:
You are passing an error if(__error(error)) which is not defined there.
You are calling this.emit in the callback function scope and it is pointing to the db.query and not the EventEmitter
You have to bind this to your callback.
Doing the following will work for you db.query('select something', callback.bind(this));
But you also have to fix your "error" mentioned in number one.
In most of your code, you are using the keyword thisin the wrong context. Every function declared with the function keyword, has its own this context, so when inside __error for example, the thisyou are referring to is not the LoadFun this and so it is not the class extending the EventEmitter class therefore does not emit anything.
You can either bind your functions when calling them, or use arrow functions, or assign to another variable, example with assigning this to another variable :
function Loadfun(param1, param2, db){
var self = this;
function __error(error, row){
if(error){
self.emit('error', error);
return true;
}
if(row.length < 1)
self.emit('failure');
}
function doSomething(){
db.query('select something', callback);
}
function callback(err, result){
if(__error(err))
return false;
else
self.emit('success', result);
}
self.doSomething = doSomething;
};
Here's an example of something I'd like to simplify:
exports.generateUrl = function (req, res) {
var id = req.query.someParameter;
var query = MyMongooseModel.findOne({'id': id});
query.exec(function (err, mongooseModel) {
if(err) {
//deal with it
}
if (!mongooseModel) {
generateUrl(Id,
function (err, text, url) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
return;
}
var newMongooseModel = new AnotherMongooseModel();
newMongooseModel.id = id;
newMongooseModel.save(function (err) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
} else {
res.send({url: url, text: text});
}
});
});
} else {
//deal with already exists
}
});
};
I've seen other SO answer where they tell you to use named functions, but don't say how to deal with variable you want to pass in or use jQuery's queue. I do not have the luxury of either.
I understand that I can replace my anonymous functions with names functions, but then I would need to pass arounds variables. How would my inner function access res for instance if the function is defined elsewhere?
The core to your question is:
I understand that I can replace my anonymous functions with names functions, but then I would need to pass arounds variables. How would my inner function access res for instance if the function is defined elsewhere?
The answer is to use a function factory.
In general, this:
function x (a) {
do_something(function(){
process(a);
});
}
can be converted to this:
function x (a) {
do_something(y_maker(a)); // notice we're calling y_maker,
// not passing it in as callback
}
function y_maker (b) {
return function () {
process(b);
};
}
In the code above, y_maker is a function that generates a function (let's call that function's purpose "y"). In my own code, I use the naming convention .._maker or generate_.. to denote that I'm calling a function factory. But that's just me and the convention is in no way standard or widely adopted in the wild.
So for your code you can refactor it to:
exports.generateUrl = function (req, res) {
var id = req.query.someParameter;
var query = MyMongooseModel.findOne({'id': id});
query.exec(make_queryHandler(req,res));
};
function make_queryHandler (req, res) {
return function (err, mongooseModel) {
if(err) {
//deal with it
}
else if (!mongooseModel) {
generateUrl(Id,make_urlGeneratorHandler(req,res));
} else {
//deal with already exists
}
}}
function make_urlGeneratorHandler (req, res) {
return function (err, text, url) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
return;
}
var newMongooseModel = new AnotherMongooseModel();
newMongooseModel.id = id;
newMongooseModel.save(make_modelSaveHandler(req,res));
}}
function make_modelSaveHandler (req, res) {
return function (err) {
if (err) res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
else res.send({url: url, text: text});
}}
This flattens out the nested callbacks. As an additional benefit, you get to properly name what the function is supposed to do. Which I consider good practice.
It also has the added advantage that it is significantly faster than when using anonymous callback (either with nesting callbacks or with promises, though if you pass named functions to promise.then() instead of anonymous functions then you'll get the same speed up benefits). A previous SO question (my google-fu is failing me today) found that named functions are more than twice the speed (if I remember correctly it was more than 5 times faster) of anonymous functions in node.js.
Use promises. Using Q and mongoose-q it would give: something like that:
exports.generateUrl = function (req, res) {
var id = req.query.someParameter;
var text = "";
var query = MyMongooseModel.findOne({'id': id});
query.execQ().then(function (mongooseModel) {
if (!mongooseModel) {
return generateUrl(Id)
}).then(function (text) {
var newMongooseModel = new AnotherMongooseModel();
newMongooseModel.id = id;
text = text;
newMongooseModel.saveQ()
}).then(function (url) {
res.send({url: url, text: text});
}).fail(function(err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
});
};
Named functions will be executed within the same scope that the anonymous functions are and would have access to all of variables you are currently using. This approach would make your code less nested and more readable (which is good) but would still technically be in "callback hell". The best way to avoid situations like this is to wrap your asynchronous libraries (assuming they don't already provide promises) with a promise library like Q. IMO, promises provide a much more clear picture of the execution path.
You can avoid the predicament of not knowing where variables came from by binding parameters to your named function using bind, for instance:
function handleRequest(res, err, text, url) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
return;
}
var newMongooseModel = new AnotherMongooseModel();
newMongooseModel.id = id;
newMongooseModel.save(function (err) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
} else {
res.send({url: url, text: text});
}
});
}
...
generateUrl(Id, handleRequest.bind(null, res));
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
// accept a callback function to execute after getting results...
function searchCoords(callback){
var result = result;
connection.query('SELECT * FROM monitoring', function(err, result){
if(err){
console.log(err);
}
// run the callback function, passing the results...
callback({result: result});
});
}
// call like this...
searchCoords(function(resultsObject){
console.log(resultsObject.result)
});
That's my code, I have an anonymous nested function which returns a variable to the outside by using a callback function. However, the variable returned (result) is an array which i need to use with googlemaps api in node.js. how can i turn this:
searchCoords(function(resultsObject){
console.log(resultsObject.result)
});
into a variable that will contain the whole array so i can call it from themarkers option in the api
Instead of passing a callback to the function why not pass an array you can append to?
function start() {
var myCoords = [];
searchCoords(myCoords);
otherFunction(myCoords); // This function does stuff with myCoords
}
function searchCoords(myCoords){
connection.query('SELECT * FROM monitoring', function(err, result){
if(err){
console.log(err);
}
else {
myCoords.push({result: result});
}
});
}
ok, this would be the code right now:
function searchCoords(myCoords){
var result = result;
connection.query('SELECT * FROM monitoring', function(err, result){
if(err){
console.log(err);
}
myCoords.push({result: result});
});
}
which i call from main.js like this:
function start() {
var myCoords = {};
myCoords = database.searchCoords(myCoords);
console.log(myCoords);
//otherFunction(myCoords); // This function does stuff with myCoords
}
this is close to the solution i'm trying to get, but still doesn't work.. instead, console shows
TypeError: Object #<Object> has no method 'push'
I need a simple function to take the result given by the anonymous nested function and turn it into a variable that i can manipulate more easily, so i can add it to the marker param in google maps api
I'm trying to refactor some complexity into a function called getData but code that calls this function doesn't seem to be getting the results.
function getData(sql) {
pool.getConnection(function(err, connection) {
if (err) return;
connection.query(sql, function(err, rows) {
if (err) return;
if (rows.length > 0) {
console.log(rows); // This outputs result from table
return rows;
} else {
return [{"error":"Not found"}];
}
});
connection.end();
});
}
However, when it is called from a function like this, I get undefined returned, even though code inside the function works fine.
app.get('/1/employees/past', function(req, res, next) {
var rows = getData("select * from users");
res.json(rows);
})
Your return is returning from the inner function, which doesn't affect the outer function.
You'd need to capture it and return it, and seeing as it appears to work with callbacks, you'd need to pass an additional callback to getData().
Something like this...
function getData(sql, callback) {
// ...
connection.query(sql, function(err, rows) {
// ...
callback && callback(rows); // etc
});
// ...
}
If you wanted to be safer, ensure the callback implements [[Call]] with typeof (link to my own blog post).
Your getData function does not return anything - the return statements inside the code are all for the anonymous functions which you pass into connection.query and connection.query.
Your connection-related function are asynchronous - i.e. they return immediately after you call them without waiting for any results to be available. If you want to do something with the results which get returned you need to do it inside one of the anonymous functions, rather than trying to do it as soon as getData completes.
The getData function is not returning anything! The getConnection function inside the getData function is calling a function which calls a function which returns something; the getData function itself is not returning anything.
What's more, if this code executes asynchronously, all the returns will execute long after the getData function has returned.
You're either going the synchronous return route or the asynchronous callback route, you can't mix both styles.