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 8 years ago.
I'm using the opentok SDK for video chatting, and I need to create sessions. It's pretty straightforward, and that part's working fine. This is all being done in node.js, server side.
The issues is - and it's mostly cause I still don't quite get var scopes (especially with anonymous functions and closures) - I have a value inside of my anonymous function that I want to access (preferably by assigning it to another var, one that's in its parent scope), but can't!
Something like this:
function generateSession(session){
var session='';
opentok.createSession(function(error, sessionId){
if (error) {
throw new Error("Session creation failed.");
}
session = sessionId;
});
return session;
}
session retains it's initial value of '' (empty string), not the sessionId it was assigned to. Help?
This isn't a question about scope. It's about asynchronicity. Your anonymous function will update the session variable in the parent function; but because your anonymous function is asynchronous, it'll happen after generateSession has returned.
Instead, you'll need to modify generateSession to accept a callback, and execute the callback (passing the generated session), once it's completed;
function generateSession(session, cb){
opentok.createSession(function(error, sessionId){
if (error) {
throw new Error("Session creation failed.");
}
cb(sessionId);
});
}
generateSession(blahblahblah, function (session) {
// Access session here.
});
This is the exact same problem as for How do I return the response from an asynchronous call? (in that situation, it's the "success" callback that is asynchronous); there might be a more suitable duplicate, but I can't find one :(. It'll still be beneficial to read through it though.
createSession is an asynchronous function. Its callback is not executed until some time after generateSession has finished. You need to pass in a callback instead:
function generateSession(cb){
opentok.createSession(cb);
}
generateSession(function(err, sessionId) {
if (err) throw err;
// use sessionId
});
Related
Reworded:
A common pattern is to pass callback functions, such as with Mongoose's save (just for example and simplified - no error handling):
someMethod(req:Request, res:Response){
document.save( function(err){ res.status(200).send({message: 'all good'})});
}
I'd like to externalize the callback. You can do this this way:
var respond = function(err:any, res:Response){
res.status(200).send({message: 'all good'});
}
someMethod(req:Request, res:Response){
document.save( function(err){ respond(err, res)});
}
...but ideally I'd like to do this by just passing a function like respond without having to create a call back function to enclose respond. I wanted to know if this is possible. Since the anonymous function has access to res, I thought there might be some way to gain access to res in a function defined externally. It appears there is not a way to do this so I'll live with wrapping it.
My original question was trying to isolate the specific issue I was interested in - which is to gain access to the caller's variables implicitly. Doesn't seem like that is possible. Fair enough.
Original Question:
I'd like to externalize a bit of code I use frequently and I'm having trouble understanding closure in the context of a Typescript method. Take a look:
var test = function(){
console.log("Testing external: "+JSON.stringify(this.req.body));
}
class Handler {
static post(req: Request, res: Response){
(function(){
console.log("TESTING anon: "+JSON.stringify(req.body));
}) ();
test();
}
}
Besides the fact that this does nothing useful, in this bit of code, the inline anonymous function has access to the req object, but the test() function does not. this in test is undefined. Removing this to match the inline function doesn't help.
I believe if I were to bind on this for the call I'd just end up with a reference to the Handler class when I really want to bind on the post method.
My motivation for doing this is that I want to make a function that can be passed as a callback to a bunch of different request handlers. When I write the functions inline it all works, but when I externalize it I can't get a closure over the variables in the enclosing method. I've read "You Don't Know JS: this & Object Prototypes", and in pure Javascript I can manage to make these sorts of things work but I'm obviously doing something wrong here (it may not be Typescript related, maybe I'm just messing it up).
So bottomline - is there a way I can externalize the handler and get access to the method variables as if I were writing it inline? I could just create an inline anonymous function as the callback that calls the external function with all the variables I need, but I want to really understand what is happening here.
This is not an answer, but will hopefully give me enough feedback to give you one because its not at all clear what you're actually trying to accomplish here and whether or not you actually understand what the terms mean is an open question since you use them correctly one minute and sketchily the next.
var test = function(){
console.log("Testing external: " + JSON.stringify(this.req.body));
}
In strict mode this will throw an error, in sloppy it will try to access the req property of the global object which is not likely what you want.
(function(){
console.log("TESTING anon: "+JSON.stringify(req.body));
}) ();
The IFFE wrapper is completely unnecessary, it literally adds nothing to the party. So why include it?
static post(req: Request, res: Response){
console.log("TESTING anon: "+JSON.stringify(req.body));
test(); // is this the spot where you are 'in-lining?'
}
What I think you want is this:
var test = function(reqBody) {
console.log("Testing external: " + JSON.stringify(reqBody));
};
class Handler {
static post(req: Request, res: Response) {
test(req.body);
}
}
I am quite new to the world of async. I am trying to do everything with callbacks at first before using any of the libraries out there. I think I have a closure problem, but don't know what to do about it.
Here is some code:
namespace.on('connection', function(socket){
var newClient = socket.id//just in case the a new user logged on between declaration and use
socket.join('room1')
function newConnection(positionCallback, hashCallback, newUser){
namespace.to(socket.id).emit('hello', {yo:'works'})
for(var i=0; i< cardCounter ;i++){
var keyVal = 'card:'+ cardArray[i]
redis.hgetall(keyVal, function (err, storedMsg) {
namespace.to(socket.id).emit('hello', {yo:'doesnt work'})
hashCallback(storedMsg, newUser)
});
if(i==cardCounter-1){
positionCallback()
}
}
}
function onConnectionComplete(){
namespace.to(socket.id).emit('hello', {yo:'works'})
}
function onHashComplete(hashObject, newUser){
namespace.to(newUser).emit('hello', {yo:'doesnt work'})
}
newConnection(onConnectionComplete, onHashComplete, newClient)
}
I have placed some socketio emits around the place to pinpoint where things go wrong.
Any emits outside of the call to redis work as expected. As soon as I go inside that anonymous function - nada.
That said, I have console.log()'ed everything inside that function. I get the right results from redis, I have the right user, and namespace is defined.
I just can't emit the result.
I would have thought that the anonymous function had access to the scope just outside it - but not the other way around. I don't see what is not making it across...
Also I know that some people don't like the if statement to invoke a callback, but that might be a discussion for another day.
It was a closure problem! Just not what I was expecting.
I also, ahem, don't really understand it.
The callback isn't the problem, getting variables accessible inside the redis response function is. I would have thought that any variables declared in a parent/ancestor function are available to children/decedent functions.
In this instance I needed to create a function inside the loop, and explicitly pass variables for it to be available inside a closure.
Here is the stackoverflow question that pointed me in the right direction:
How can I access the key passed to hgetall when using node_redis?
I'm working on making a modification to a node.js module to improve error handling for one of my uses cases. The specifics of the module aren't really relevant, but one of the things I want to do is trigger a delayed retry when receiving a certain status code from the response to an API request. To do this I'm using the timeOut function to trigger a new call to the function after a period of time.
The structure of the code looks like this:
Outer function (parameters specified by client application)
——API request (using parameters)
——Callback from API request (response with status code)
——If error, set timeout to call outer function after delay
However, I also want to handle the case that the outer function is called again while waiting for the timeout. I don't want any calls to trigger a new API request while a timeout is pending, but I do want the parameters from the most recent call to be used when the timeout finishes.
I've been able to get this working using variables that are global to the module. Each time a new call comes in to the outer function it updates a global object with the new parameters then, if a timeout is pending, returns without calling the API request. The timeout function uses the parameters from the global object to call the outer function, rather than the parameters from when it was set. This way it always uses the most recent values that were passed into the outer function, but doesn't make duplicate API calls.
Here's a simplified example of how I've achieved this behavior with global variables: JSFiddle. Hit run a few times until you get a "failure response" which then triggers the timeout.
This works, but I would prefer not add these global variables into the module if there's a better way.
Is there any way to get this same behavior but have all of the state encapsulated in the outer function without using globals? I'm also open to completely rethinking the way I'm handling this if anyone has ideas.
You're not going to be able to do this without creating variables outside of your outer function, however it's still possible to create those variables without polluting your global scope.
To do so, wrap your outer function in another function that gets executed immediately, sometimes known as an IIFE:
mainFunction: (function() {
var savedParam1;
var savedParam2;
var savedParam3;
var pendingTimeout = false;
return function(param1, param2, param3) {
savedParam1 = param1;
savedParam2 = param2;
savedParam3 = param3;
if (pendingTimeout) {
log('pending timeout -- ignoring new call');
return;
}
/* and so on */
}
})(); // the () causes the outer function to run immediately,
// which creates a scope for the savedParam / pendingTimeout variables,
// and then returns the inner function (your old outer function)
// to be used for mainFunction
I have a problem with a "static" function in javascrip (nodejs server).
User.create = function(data, _callback){
var node = db.createNode(data);
var _user = new User(node);
_user.save(function(err){
if(err) return callback(err, null);
_user.index(function(err){
if(err) return callback(err, null);
callback(null, _user);
})
})
};
If I call this function twice the _user variable in the internal callback function takes the new value, it seems it overrides the function var instead of allocate a new one.
I need calling this function to allocate a new variable, so it waits save and index functions to complete without changing _user variable.
JavaScript variables are indeed function scoped, so there wouldn't be any explanation for var _user not defining a new variable on subsequent runs.
Looking at the code, I would be more suspicious of what's happening in your User constructor - perhaps it contains some scoping or other logical issues resulting in identical users being created on subsequent calls. Similar "suspects" would be the data parameter getting passed in, as well as db.createNode(). Only suggesting these areas, because it's more likely that there's a programmatic issue at play, rather than JavaScript not following the rules :)
Also, I noticed that your User.create function accepts a parameter called _callback, but later on is invoking callback. I don't know if that's a typo in your example, or if you're accidentally invoking a callback from a higher scope not shown in the example, but that could produce weird behavior.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Javascript OOP return value from function
I have a class defined like this
function SocialMiner(tabUrl)
{
var verbose=true;
var profileArray=new Array();
this.tabUrl=tabUrl;
this.getTabUrl=function(callback)
{
chrome.tabs.getSelected(null, function(tab)
{
callback(tab.url);
});
}
this.setTabUrlValue=function(pageUrl)
{
this.tabUrl=pageUrl;
console.log("22"+this.tabUrl); //this statement shows url correctly
}
}
When I call this method like these
miner.getTabUrl(miner.setTabUrlValue);
miner.logToConsole("1"+miner.tabUrl); //This statement returns undefined
The console.log inside callback correctly outputs url , however, the tabUrl property of miner ojbect is undefined , as seen in second console.log. Why is it so ?
The solution is to save a reference to this within the constructor (available later on via closure):
var that = this; //in the top of the SocialMiner constructor function
and in setTabUrlValue use:
that.tabUrl=pageUrl;
I suspect running a method as a function (callback) loses scope, i.e. doesn't know of any this anymore. In other words, it runs within the scope of the constructor, not as a method of the instance using it. A variable referencing this in the constructor scope is available to the function, and that points to the right this on instance creation.
You could also force callback to run in the current instance scope like this:
callback.call(this,tab.url);
In that case you can leave this.tabUrl=pageUrl; as it is.
This is an simplification of your code. The methods return this to be able to directly reference a property of the instance (see console.log last line):
function Some(){
var that = this; // note: not used in this example
this.getA = function(callback){
someval = 'foobar';
callback.call(this,someval);
return this;
};
this.getB = function(val){
this.val = val;
return this;
};
}
var some = new Some;
console.log( some.getA(some.getB).val ); //=> foobar
Taking a look # your code again, I think you're loosing scope twice, because callback is called from within another callback. That's why I think your code on that spot should be:
chrome.tabs.getSelected(
null,
function(tab) {
callback.call(that,tab.url); //< use that here
}
);
Furthermore, in you code # github, I don't see any instantiation of the miner instance.
this is a tricky beast in JavaScript and as others have pointed out is the key to the issue. The problem with using this everywhere is that it's value can change depending on who/where the function is called from (for example, see the call and apply methods in JavaScript). I'm guessing that if you wrote the value of this to the console in the the callback from the chrome.tabs.getSelected function you'd find it isn't your miner any more.
The solution is to capture a reference to the this that you're actually interested in when you know for sure it's the right one & then use that reference from then on. Might make more sense to see it commented in-line in your example:
function SocialMiner(tabUrl)
{
//At this point we know "this" is our miner object, so let's store a
//reference to it in some other (not so transient) variable...
var that = this;
var verbose=true;
var profileArray=new Array();
this.tabUrl=tabUrl;
this.getTabUrl=function(callback)
{
chrome.tabs.getSelected(null, function(tab)
{
//at this point "this" is whatever the "chrome.tabs.getSelected"
//method has decided it is (probably a reference to the tab or something)
callback(tab.url);
});
}
this.setTabUrlValue=function(pageUrl)
{
//because this can be called from anywhere, including the chrome callback
//above, who knows what "this" refers to here (but "that" is definitely
//still your miner)
that.tabUrl=pageUrl;
console.log("22"+that.tabUrl);
}
}
You can see how much this shifts around in libraries that use callbacks heavily like jQuery, where often this is set to convenient values, but certainly not the same this that was logically in scope when you made the initial call.
EDIT: Looking at the full source (& example) you posted, this is just a timing issue where obviously the chrome.tabs.getSelected is returning asynchronously after your "second" call to log goes through...
console.log("5");
miner.getTabUrl(miner.setTabUrlValue); //setTabUrlValue is logging with '22'
console.log("6");
miner.logToConsole("1"+miner.tabUrl);
console.log("7");
// Output:
5
6
1 undefined //the chrome.tabs.getSelected hasn't returned yet...
7
22 http://url //now it has (so if you tried to use miner.tabUrl now you'd be all good...
The solution is to put all the stuff after the get/set into the callback, since you don't want anything happening until after that tabUrl is finished being set... so something like this:
console.log("5");
miner.getTabUrl(function(pageUrl) {
miner.setTabUrlValue(pageUrl);
console.log("6");
miner.logToConsole("1"+miner.tabUrl);
console.log("7");
});
Hopefully that will see you getting your results in the order you expect them.
I think this happens because closure vars do not survive a function call.