I tend to write a function like this:
library.getCookie(request.headers.cookie,function(cookies){
db.query("SELECT name,lastupdate FROM session WHERE id=?",[cookies.SESSID],function(result){
var action = require('./action');
new action({
data : result,
callback : function(){ // this callback's gonna run at the end of the request, after severals other db queries.
require('fs').rename('/session/'+cookies.SESSID,'/session/'+require('crypto').randomBytes(20));
}
}).build();
});
});
I got a feeling that my cookies.SESSID is vunerable to race condition because it will be shared between severals IO events. If my concern is feasible, how can I fix my code?
Declare the variables you want to pass to the more inner callback with var and you will stay safe. Otherwise you are just declaring them as implict global variables and each call of the function will overwrite the value you are expecting to get in the callback;
(the callback inside action could take the value of cookies not like the one you passed when you called the first time but a new value if the function is invoked before the first callback is solved)
I have just answered a similar question, with examples: https://stackoverflow.com/questions/25229739/race-condition-and-common-mistakes
Related
I am reviewing a sample nodejs server code, which is working fine. but could not understand the following code:
var handlers = {};
// Creating a sample handler
handlers.sample = function(data,callback){
callback(406,{'name':'sample handler'}); // How is this line of code working??
};
// Creating a not found handler
handlers.notFound = function(data,callback){
callback(404); // How is this line of code working??
};
In the entire code there is no implementation of "callback" function then how
callback(406,{'name':'sample handler'});
and
callback(404);
are working?
Please suggest. Thanks.
callback isn't implemented in the code you posted; it's a named parameter.
To call one of the functions that requires a callback, you'll need to pass it in as an argument, like:
handlers.sample("some data", () => console.log("I'm in a callback!));
The first argument ("some data") goes in the data parameter, and the second argument (() => console.log("I'm in a callback!)) goes in the callback parameter. When callback(404) is run, it executes the callback function (in the above example, it would do console.log("I'm in a callback!)).
Basically, when you call handlers.sample, you should pass a function as your second argument, and that function will be called, usually asynchronously, and presumably after something is done with the data you pass as the first argument. For example, based on the code you provided:
handlers.sample(dataObject, (number, object) => {
console.log(number)
console.log(object.name)
})
would yield this result in the console:
406
sample handler
I’m curious if this is a public library you are seeing this code in, so we can take a closer look?
I did a further digging in into this and found that
selectedHandler
in the following code (this is not mentioned in the question) is getting resolved into handlers.sample or handlers.notFound variable names based on some logic (which is not mentioned here)
selectedHandler(data,function(status,payloadData){
// somelogic with the status and payloadData
});
And second parameter of this function which is a complete function in itself
function(status,payloadData){
// somelogic with the status and payloadData
}
is going as the second parameter in handlers.sample or handlers.notFound which is a Callback. So execution of Callback in the current context is execution of this function (this function is anonymous because it has no name and getting executed as Callback)
I'm unable to get the values in meals object although i have create new object at the top can any one tell which is the best procedure access variable inside callback function
var meals = new Object();
passObj.data = _.map(passObj.data, (x)=> {
x.mealImageUrl = !_.isNull(x.image_url) ? `${config.image_path}${x.image_url}` : x.image;
dbHelpder.query(`select * from meals where meal_category = ${x.category_id}`,(error,result)=>{
meals = x.result;
passObj.total = 555
});
return x;
});
You need to use callback again inside the callback function. :)
You are doing something asynchronous, it means, there are no sequence codes. (At least, I keep this in my mind, don't know how others think about this.) So, the code should be:
function somehow(callback) { // you get the result from callback
var meals = new Object();
passObj.data = _.map(passObj.data, (x)=> {
dbHelpder.query(`select * from meals where meal_category = ${x.category_id}`,(error,result)=>{
meals = x.result;
passObj.total = 555;
callback(meals); // Here you get the result
});
}
return x;
}
So, when you are going to use this function, it should be
function afterMeals(resultMeals) {
// do something on the meals
}
somehow(afterMeals);
Use some other technology can make it a bit clear (like promise), but you can not avoid callback actually.
First of all, I cannot see what passObj exactly is, apparently it is defined elsewhere.
Secondly, callback functions don't function the way you seem to think they do. Typically one reason to use them is to implement asynchronous calls, so returning a value is not of use.
The idea is as follows. Usually you have a call like this:
var myFunc1 = function(){
return 42;
}
var x = myFunc1();
myFunc2(x);
However when myFunc1 is an asynchronous call returning a value is impossible without using some sort of promise, which is a topic on its own. So if myFunc1 was an asynchronous call and the 42 was returned e.g. by a server, then just returning a value caused the value to be null, because the return value is not calculated and received yet, when you arrive at return.
This is a reason for callbacks to be introduced. They work in a way, that allows for asynchronous calls and proceeding the way you want to after the call has finished. To show on the example above:
var myFunc1 = function( myFunc2, params ){
// do async stuff here, then call the callback function from myFunc1
...
myFunc2(x);
}
So the asynchronous function doesn't return anything. It makes the calls or calculations it needs to make and when those are done (in the example that is when x has been declared and assigned a value) myFunc2, which is the callback function in our example, is called directly from the asynchronous function.
Long story short - do what you need to do with x directly inside the callback function.
First of all, I'm trying to test the second time a function being called returns the correct value. To be more specific, I'm trying to test the second time an event is received by socket.on() returns the correct value.
I know that Sinon.spy() can detect whether a function being called or not. But it seems not working with socket.io events.
I'm trying to test for example,
var socketio = io.connect(someUrl);
socketio.on(eventName, cb);
if the 'eventName' is called. I tried
var spy = sinon.spy(socketio, 'on');
assert(spy.withArgs(eventName).called);
But it says it's never being called.
Furthermore, what if I'd like to test the data in the callback function is right or not, for example
var socketio = io.connect(someUrl);
socketio.on(eventName, function(data) {
data.should.equal(something);
});
Is it possible to test that?
Update:
I solved the problem by adding a count in callback
var count = 0;
socketio.on(eventName, function(data) {
count++;
if(count === 2) {
data.should.equal(something)
}
}
But I think this is a hacky way, is there any standard/smart way to do it(if possible using sinon)?
You don't want to spy on on -- that's only called once, at the time you set up the listener. (Unless you actually want to spy to see if the listeners are being set up at all, and don't care if they're actually used. That doesn't appear to be what you want here.)
Instead, you want to spy on the cb callback function, to see if and how it runs.
var callbackSpy = sinon.spy(cb);
Then, pass that spy in as the callback:
socketio.on(eventName, callbackSpy);
And test if the callback was ever run with the desired argument value:
assert(callbackSpy.calledWith(something));
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 6 years ago.
I'm a NodeJS beginner and actually I try to work with a NoSQL Database for building a simple login via. Digest Authentication. I also created an module called "users" wich is loaded in my application.
Now when I try to call the data results inside the callback function I get my correct data. In the same callback I assign the returned data to the variable records. When I call the variable outside I will get the result null - where is the error?
var dbdriver = require('nosql'),
dbfile = dbdriver.load("./db.nosql"),
records = null
dbfile.top(1000).callback(function(err, response) {
(function(users) {
records = users
console.log(users) // Here i get the wanted result
})(response)
})
console.log(records) // Here the result is empty
thank you very much :)
Since the you are calling console.log(users) inside the callback, the value is reflected correctly. And this is a behavior of asynchronous calls in javascript. So the last line console.log(records) would probably be run first before the callback executes and so records up to this point does not have any value.
So I would suggest that you make the a function that does something on users and call it inside the callback.
var dbdriver = require('nosql'),
dbfile = dbdriver.load("./db.nosql"),
records = null
dbfile.top(1000).callback(function(err, response) {
(function(users) {
//records = users
//console.log(users) // Here i get the wanted result
doSomethingToUsers(users);
})(response)
})
function doSomethingToUsers(users) {
// do something to users
}
//console.log(records) // Here the result is empty
Looks like a timing issue to me. When console.log(records) in the outer scope gets called before the (async) callback function was called, records contains exactly the value that was initially assigned (records = null).
Only after calling the async callback, the records variable will be set.
Does this help?
I was trying to solve the "Juggling Async" problem in the "learnyounode" workshop from nodeschool.io. I saw a lot of questions here about this problem where it is not working because the url's were being called from a loop. I understand why that wouldn't work.
I had tried something like this.
var http=require('http');
console.log(process.argv[2],process.argv[3],process.argv[4]);
fn(process.argv[2],
fn(process.argv[3],
fn(process.argv[4])));
function fn(url,callback){
http.get(url,function(response){
var string='';
response.setEncoding('utf8');
response.on('data',function(data){
string+=data;
});
response.on('end',function(){
console.log(url,string);
if (callback) {
callback();
}
});
});
};
As per my understanding, the second GET call should go out only after the first one has ended, since it is being initiated after response end. But the output is always in different order.
I have seen the correct solution. I know that doing it this way fails to leverage the advantage of async, but shouldn't the callbacks make it output in order?
When you pass in a function as a callback, you need to only pass in the function name or function definition. By providing the arguments, you are actually calling the function.
E.g. let's say you had a function f1 that took in another function as a parameter, and another function f2 that you want to pass into f1:
function f1(func_param) {
console.log('Executing f1!');
}
function f2(a_param) {
console.log('Executing f2!');
}
When you do the following call (which is similar to what you are doing, providing a callback function and specifying parameters for the callback):
f1(f2(process.argv[2]));
You're evaluating f2 first, so 'Executing f2!' will print first. The return of f2 will get passed as a parameter into f1, which will execute and print 'Executing f1!'
In your call
fn(process.argv[2],
fn(process.argv[3],
fn(process.argv[4])));
You are saying, let's pass the result of fn(process.argv[4]) as a callback to fn(process.argv[3]), and we'll get the result of calling that and pass that as a callback to fn(process.argv[2]).