Nodejs give callback with in the callback function - node.js

I want to do something like that:
callbackfunc=function(client, id, callback){
callbackfunc2({'id': id}, myfunc);
}
var myfunc = function(err,result){
callback(new Error("Ein Fehler"));
}
callbackfunc(client, 38373,function(err, data){
console.log("Here:"+data+" Error:"+err);
});
How can I make the callback available in myfunc? I always get callback is not a function. If I write it his way it works. And I don't understand why it doesn't work when I had a special function.
callbackfunc=function(client, id, callback){
callbackfunc2({'id': id}, function(err,result){
callback(new Error("Ein Fehler"));
});
callbackfunc(client, 38373,function(err, data){
console.log("Here:"+data+" Error:"+err);
});

If you want callback available in a separate function, then you have to either:
Pass it to myfunc as an argument.
or
Declare myfunc inside the scope where callback is available.
It works in this example:
callbackfunc=function(client, id, callback){
callbackfunc2({'id': id}, function(err,result){
callback(new Error("Ein Fehler"));
});
because you are calling callback from within the scope in which it is defined. Remember function arguments are only available within the block of the function in which they are defined. They are not available outside that function unless you pass them to some other scope as an argument or assign them to some other variable that is itself available in a different scope.

Related

Node Js passing parameters to an "off the shelf" callback

I have a library that makes a web request and passes the results to a callback like the sample below:
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, callback);
Then the callback is something like:
var callback = function (data) {
console.log(data);
};
Is there a way I can pass more arguments to the callback from the caller given this is an off the shelf ready library?
just define your "first" callback inline, there you can pass more parameters:
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, function(data) {
callback(data, otherVar1, otherVar2);
});
Then your callback could look something like this:
var callback = function (data, otherVar1, otherVar2) {
console.log(data);
console.log(otherVar1);
console.log(otherVar2);
};
Without editing the code of the library that calls the callback, you can't change what is passed to that specific callback. But, there are multiple options where you can get access to other variables:
Inline callback accessing variables in scope
you can use scope and an inline definition to make other variables available (via scoping) to the callback.
let someVar1 = "foo";
let someVar2 = 4;
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, function(data) {
// can access data, someVar1 and someVar2 here as they are all in scope
console.log(someVar1);
});
Use .bind() to add arguments to the callback
You could also use .bind() to create a new function that adds parameters.
function callback(someVar1, someVar2, data) {
console.log(someVar1);
}
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, callback.bind(null, "foo", 4));
.bind() creates a new function that adds additional arguments before calling the actual callback() function.
Make your own stub function
Or, make your own stub function that calls your callback (essentially what the .bind() option was doing for you):
function mycallback(someVar1, someVar2, data) {
console.log(someVar1);
}
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, function(data) {
mycallback("foo", 4, data);
});
I assume your callback arguments are variable and that the function is defined in an isolated manner (where it can't access scope beyond itself, and that you don't want to mutate the callback function's underlying data. It also assumes that Data is an object (You should perform some validation on this though). Therefore you could do something like the following:
someReadyWebSearchThirdPartyLibrary.getSearch(parameters, error, function(data) {
data.newArgument = 'lorem';
callback(data);
});
This way you can add any arbitrary arguments onto the data object passed.

Set variable equal to mongodb key value

var userLat = db.collection('users', function (err, document){
document.findOne({_id: loggedUserID}, function(err, docs) {
console.log(docs.currentUserLat);
})
});
This is my code, I'm trying to get the value that's console logged into the variable. I just can't find the correct syntax to do this. The console log does return the correct value just need to drop it into the variable. Grateful for some help.
What do you want to do with 'docs.currentUserLat'?
You can do what you need to do without saving docs.currentUserLat to a variable that has scope outside of your db.collection call. Some examples:
If you simply want to change the document in your database, take advantage of the many methods specified in the Collections API: http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html. For example, to update the document and simultaneously resave it in the database:
db.collection('users', function (err, document){
document.findOneAndUpdate({_id: loggedUserID},
{currentUserLat: [updated value]},
function(err, docs) {
if(err) console.log(err);
}
)
});
If you just wanted to use docs.currentUserLat inside some node function, you'll need to properly nest the document.findOne function inside a callback (or vice versa). For example, to write currentUserLat to a file using the fs module:
var fs = require('fs');
db.collection('users', function (err, document){
document.findOne({_id: loggedUserID}, function(err, docs) {
fs.writeFile("pathToYourFile", docs.currentUserLat, function(err) {
if(err) {return console.log(err);}
});
});
});
Or, if you want to send it in response to a simple http request:
var http = require('http');
http.createServer(function(request,response){
db.collection('users', function (err, document){
document.findOne({_id: loggedUserID}, function(err, docs) {
response.writeHead(200,{'Content-Type':'text/html'});
response.end(docs.currentUserLat);
});
});
});
The key thing to remember is what JohnnyHK said in their comment: docs.currentUserLat is only available inside the anonymous function passed to findOne. So, whatever it is that you need to do, do it inside this function.
(Reading the link JohnnyHK provided is a great way to get started with understanding asynchronous functions in Node. Another is https://github.com/rvagg/learnyounode)
First of all you have to understand how javascript callback works. After that you will see that nothing assigns docs.currentUserLat to your userLat variable. The reason behind this is that your docs.currentUserLat is available only inside the callback. Think about it in the following way:
You program started to execute and encountered the line: var userLat = .... This line tells: do a callback (which basically asks someone else to do the job), your while your job is being executed the program continues, by assigning userLat to undefined and executes further. Then at some period of time callback finishes and console.log your docs.currentUserLat.
One way to have the desired behavior is to make userLat global and instead of console.log(docs.currentUserLat); do userLat = docs.currentUserLat. The problem that if you will do this, your userLat eventually will have the desired value (if callback will not fail), but you can not predict when. So if you will do
var userLat = db.collection('users', function (err, document){ ... });
.. some other code
console.log(userLat);
you will not be sure that you will get the output. Another way to do put everything in another callback.

Node.JS MySQL query nested function returning array

function searchCoords(){
var result = result;
connection.query('SELECT * FROM monitoring', function(err, result){
if(err){
console.log(err);
}
return{
result: result};
});
}
That's my code. I'm using that code to find the last coordinates of some devices and display them in google maps. but i need to first be able to access the array from the ouside so i can do something like:
myModule.searchCoords().result
or
myModule.searchCoords()().result
However I still can't access the array (result) from the outside function, let alone from another module.
I've been reading on closures, scopes, nested functions, anonymous functions, etc. but i still can't find the solution. What am i doing wrong?
Problem is, that the query is asynchronous, so it can't return a value in the normal flow. If you pass a function when you call searchCoords, then that function can be called after the results come back - which could be after a long delay. This is necessary to prevent the program flow from being blocked whilst potentially long operations are in process.
// 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...
// pass a function accepting results object that will be executed as callback
// once results have been returned...
searchCoords(function(resultsObject){
console.log(resultsObject.result)
})

relationship between callbacks in node.js

This question arises from a homework question in the 10gen MongoDB class which uses Node.js, however, I'm not asking you to solve the homework (which is about Mongo) rather to explain relationship between two functions. In a session.js file, the method addUser is called on a users data object module like this
users.addUser(username, password, email, function(err, user) {
"use strict";
if (err) {
// this was a duplicate
if (err.code == '11000') {
errors['username_error'] = "Username already in use. Please choose another";
return res.render("signup", errors);
}
// this was a different error
else {
return next(err);
}
}
sessions.startSession(user['_id'], function(err, session_id) {
"use strict";
if (err) return next(err);
res.cookie('session', session_id);
return res.redirect('/welcome');
});
});
In the users data object module, there's a function this.addUser like this
this.addUser = function(username, password, email, callback) {
"use strict";
// Generate password hash
var salt = bcrypt.genSaltSync();
var password_hash = bcrypt.hashSync(password, salt);
// Create user document
var user = {'_id': username, 'password': password_hash};
// Add email if set
if (email != "") {
user['email'] = email;
}
//node convention first arg to call back error, 2nd the actual data we're returning, callbackwith value if added user
// look in session.js file that uses the UsersDAO to get a sense of how this is being used and
// what type of callback is passed in.Task is to fill in code to add a user when one tries to signup
// TODO: hw2.3
callback(Error("addUser Not Yet Implemented!"), null);
}
We're supposed to implement the callback that enables the addition of the User into the mongo database. I'm not asking you to tell me that. Rather, the fourth argument to the users.addUser function is a callback function function(err, user) { }. Also, the this.addUser function has callback as the fourth parameter, so the callback function from users.addUser runs inside this.addUser? Can you help clarify the relationship between these two? I know that a callback runs when a function has finished but I don't know why it's being passed into this.addUser
so the callback function from users.addUser runs inside this.addUser?
That's exactly what is happening. As you probably already know, Javascript treats functions as first-class citizens. This means you can pass them around and return them just like other types. Functions are special because they have two additional fields in their prototype: the context in which they run and the function's code.
In your case, here is how this works:
1 - On the line users.addUser(username, password, email, function(err, user) {...}, you are calling this.addUser with the fourth parameter being an anonymous function.
2- this.addUser assigns a variable name to this anonymous function, calling it callback.
3- In the case of an error, callback is executed (since it is a function) with an error object Error("addUser Not Yet Implemented!") and null as parameters. These stand for err and user as declared in your first line.
An example that you are probably familiar with is that of setTimeout. This function is defined as:
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);. The method setTimeout does not really care what func is, as long it can execute it. Assume the following code, for example:
var delay = 30 * 1000;
document.getElementById("foo").innerHTML = "This text will change in "+delay/1000+" sec";
setTimeout(function(){
document.getElementById("foo").innerHTML = "told ya!";
}, delay);
Here, again, we are passing a callback, that will be executed in the future. One cool thing is that this callback has access to the scope of setTimeout, which means it can use the variables defined in that scope. In the case of setTimeout, this isn't really useful, but, in your this.addUser, you have access to the error object that originated from the creation operation.
For more details on how callbacks work, check out these two answers.

Query callback with arguments and synchronous queries

I have two problems implementing a RESTful service using Node.js / node-postgres lib / PostgreDB and both are due to the async nature of JS.
A) I need to pass an extra argument to a callback in client.query(query, callback) call
I am inside a callback of a query and going through an array of recently fetched rows from a DB and want to launch a subsequent query for each of them:
var query = client.query('SELECT * FROM event', queryAllEventsHandler);
function queryAllEventsHandler(err, result){
allEvents = result.rows;
/* allEvents is an JSON array with the following format
[ {"id_event":1, "name":"name of the event"},
{"id_event":1, "name":"name of the event"}
]
*/
for(var i = 0; i<allEvents.length; i++){
client.query('SELECT * FROM days where id_event = $1',[allEvents[i].id_event], function( err, result){
//I want to have a reference to variable i
}
}
In the above example I want to do something like:
client.query('SELECT * FROM days where id_event = $1',[allEvents[i].id_event], function( AN_EXTRA_ARG, err, result)
Where the AN_EXTRA_ARG is an extra argument or a closure in the callback function... How can I achieve this? Should I create an closure with the of i and pass it as a callback's arg? How ? :|
B) "Synchronizing" queries
I need to launch various queries and create a custom JSON from all of them. Since every query and it's callback are asynchronous (waiting for no one) I was looking for a way to "tame" it and among other stuff I found a solution that occured to me in the first place, but seemed a bit "bad/lousy":
Keeping the query count is really the way to go as #jslatts suggests in Synchronous database queries with Node.js?
Hope I
With regards to question A, you could create a function to handle both your queries and only return when the last query is executed and return both results to the callback.
for(var i = 0; i<allEvents.length; i++){
query(client, allEvents[i], function(result1, result2) {
//do something
});
}
function query(client, event, callback) {
client.query('SELECT * FROM days where id_event = $1',[event.id_event], function( err1, result1){
client.query('SELECT * FROM days where id_event = $1',[event.id_event], function( err2, result2){
callback(result1, result2);
});
});
}
I don't like answering my on question, but this might be of interest to someone.... Regarding the A part of my question. You can assign a custom object to this in your function.
As you know a keyword this corresponds to the Window (top) object when inside a function (unless it's a method function). Using the bind function you can change the reference of this to your own object...
So what I did was, I created a named function queryCallback
function queryCallback(err, result){
//this == Window (default)
}
changed the anonymous callback function to the named one queryCallback:
client.query('SELECT * ... where id_event = $1',[allEvents[i].id_event], queryCallback.bind( {"position":i}, err, result));
Now, note queryCallback.bind( {"position":i}, err, result));
What bind(my_custom_this, [other args]) does is it binds a custom object (in my case {"position":i}) to this inside the function upon which the bind was called...
Now we have this scenario:
function queryCallback(err, result){
//this == {"position":i}
}
Bind explained: http://fitzgeraldnick.com/weblog/26/
A) I personally like lodash (or underscore if you prefer) partial() for this. It takes a function and a number of arguments and returns a function with the provided arguments applied and the remaining arguments still open. It's very much like the functional concept of currying.
B) For combining multiple asynchronous results I highly recommend async. The syntax will take a little getting used to, but makes thing like this very easy. Quick sample:
async.parallel([
one: function(callback){
db.fetch(options, callback);
},
two: function(callback){
db.fetch(options, callback);
}
],
function(err, results){
// this callback will get called when either parallel call gives an error
// or when both have called the callback
if (err) {
// handle error
return;
}
// get the results from results.one and results.two
});
== Added in edit ==
Actually lodash also provides a nicer (imho) albeit slightly more expensive (due to function calls) solution for your problem A):
_(allEvents).each(function(event, index, array) {
client.query('SELECT * FROM days where id_event = $1',[event.id_event], function( err, result) {
// Just use 'index' here, which doesn't change during the each
}
});
For B), your options include async or via a Promise library (such as Q, when.js, Bluebird, etc...)

Resources