I have a javascript function that has a callback then an anonymous function then another callback, and something has gone wrong with the scope. The parameter callbackFunc is retaining its value from the first function call and not using the new value passed in the 2nd function call.
function IsReady(callbackFunc) {
if (!IsValid()) return false;
IsConnected(function () {
if (typeof (callbackFunc) == 'function')
callbackFunc();
return true;
});
}
function IsConnected(validCallbackFunc) {
$.post("IsConnected", function (data) {
if (data.IsValid) {
if (validCallbackFunc && typeof (validCallbackFunc) == 'function')
validCallbackFunc();
}
});
}
$('#SaveButton').click(function () {
IsReady(SaveInvoice); // works
});
$('#ExportButton').click(function () {
// works only if IsConnected() is true
// otherwise SaveInvoice is called again
IsReady(ExportInvoice);
});
function SaveInvoice() {}
function ExportInvoice() {}
In some circumstances, when I click the ExportButton, the SaveInvoice function is run instead of the ExportInvoice function. I'm guessing that it's a scoping issue - that somehow the old value of callbackFunc has been retained. But I don't quite understand it due to the mix of callback + anonymous function + another callback. I didn't write this code, but I have to fix it. Is there anything I can do to clear the value of callbackFunc at the end of IsReady()?
IsReady(ExportInvoice) works if IsConnected() is true. If IsConnected() is false then the result is that SaveInvoice() gets executed when in fact nothing should happen (because it is not connected).
There is no way that the callbackFunc value could be retained between two different calls of the IsReady function.
In your code, each time a click event handler is executed, a new scope is created when IsReady is called. Each scope has it's own local parameter callbackFunc. Each scope will define its own anonymous function passed to IsConnected where resides the callbackFunc variable enclosed in a closure.
So this is not a scope problem.
To prove it, I emulated your code here: http://jsfiddle.net/pwJC7/
In your code you talk about the IsConnected return value. This function actually does not return anything. The connection status seems to be checked through an ajax call returning an XML or JSON data with an IsValid property (emulated by $_post in the fiddle).
Maybe your issue is due to this asynchronous call. But it's impossible that you experience a call to SaveInvoice function as a consequence of a click to ExportInvoice button with the JavaScript code you provided.
Related
I'm perplexed as to why I have to wrap my alert in a function in order to avoid my code ignoring the event listener.
If I run this code, It'll work properly and I'll be able to click and have the alert display after the click
elem.addEventListener('click', (function(numCopy) {
return function() {
alert(numCopy);
};
})(num));
But if I run this code, it'll ignore the event listener and display the alert as soon as the page loads
elem.addEventListener('click', (function(numCopy) {
return alert(numCopy);
})(num));
I could ignore it and just accept this is the way it's done but I would really appreciate it if someone could explain the logic behind this so I can fully grasp the concept, thanks a lot in advance.
Lets figure out what's going on by looking at the following example:
function foo(x) {
return x;
}
var bar = foo(42);
baz(bar);
What is the value passed to baz? 42 because foo simply returns the value passed to it.
Now lets inline the function call:
function foo(x) {
return x;
}
baz(foo(42));
What is the value passed to baz here? Still 42 whether we assign the return value to a variable or directly pass it to the other function doesn't make a difference.
Now lets inline the function definition:
baz(function foo(x) { return x; }(42));
Now we have an IIFE. What is the value passed to baz here? Still 42. We didn't actually change what the code is doing, we just got rid of intermediate variable assignments (we could also drop foo from the function expression).
How does this related to your code? Lets work our way backwards from your code. We start with:
elem.addEventListener('click', (function(numCopy) {
return alert(numCopy);
})(num));
Lets extract the function definition:
function foo(numCopy) {
return alert(numCopy);
}
elem.addEventListener('click', foo(num));
Now lets extract the function call:
function foo(numCopy) {
return alert(numCopy);
}
var bar = foo(num);
elem.addEventListener('click', bar);
Do you see what's wrong here? You are calling the function (foo in this case). The function executes alert and that's why you immediately see the alert.
foo returns the return value of alert (assigned to bar), so you are passing that value to addEventListener.
But alert returns undefined whereas addEventListener expects to be passed a function.
That's why your IIFE needs to return a function.
I have the following jQuery code:
function next() {
//some code here
}
function previous() {
//some code here
}
$("#next").click(function(){
next();
});
$("#previous").click(function(){
previous();
});
This works, but this doesn't:
$("#next").click(next());
$("#previous").click(previous());
Why is this happening? Is there a problem in my code, or is this just a thing with jQuery? Note: #next and #previous refer to two buttons in my html file.
The callback should be a reference to the function.
Why $("#next").click(next()); doesn't work?
func() is a function call and not a reference, which is why it is called immediately.
This,
$("#next").click(function(){
next();
});
is a preferable way in case you need to pass arguments.
Else,
$("#next").click(next) //notice just the signature without ()
This works (if the functions next and previous are defined):
$("#next").click(next);
$("#previous").click(previous);
In this case the next and previous are also callback functions, the difference between the two is,
when you call this line
$("#next").click(next()); the function is executed immediately, and you are passing the result of the next function to the eventHandler of jQuery.
and in this case
$("#next").click(next); you are passing the function next to the EventHandler of jQuery.
Btw.: in the jQuery API Documentation (https://api.jquery.com/click/) it shows all parameters for the click function and the required types it states: "...handler Type: Function( Event eventObject ) A function to execute each time the event is triggered. ..."
try like this you will get your answer,
function next() {
//some code here
}
function previous() {
//some code here
}
$("#next").click(next);
$("#previous").click(previous);
working demo jsfiddle Example
What is going on there is a little bit obscured by the syntax of anonymous functions function() { ... }. What you are doing by that is passing a function, without calling it. And I want to explain how this works:
If you have a simple function
function next() { return 5 };
It will simply return the value 5, if you call it from somewhere:
a = next(); // value of a will be 5
But what you can do too, is to pass the whole function to a. This is possible, because functions in JavaScript are actually objects:
a = next;
b = a(); // value of b will be 5
If you look at the syntax, it shows you, that putting parentheses () at the end of a function invokes it, and returns the return value. While the naked string, without parentheses hands you the function itself.
So what is a callback now, and what does click() like to get as a parameter? A callback function is a function, that gets called later; we actually hand it over, to get called later. click() would like to get such a function as parameter, and it should be clear now, that we have to pass the function without parentheses, to enable click() to call it later, instead of just passing a 5 to it.
$("#next").click(next);
So how does then the initial syntax with the anonymous function work?
function() { next(); }
actually wraps your next() into another function, which is anonymous – because it does not have a name – but is working in the same way as a named function. You can even set a variable by it:
a = function() { next(); } // a will be the anonymous function that calls next()
But calling that function a() will return nothing, because the anonymous function does not return a value (To be exactly: every function call in JavaScript is returning at least undefined, but that's a technical detail).
It can even be called immediately by putting parenthesis at the end of it:
a = function() { return next(); }() // value of a will be 5
Adding the return there will make sure, the return value of next() will be passed through the anonymous function.
This should make clear why
$("#next").click(function(){ next(); });
is working, and why
$("#next").click(next());
is not, but
$("#next").click(next);
will be a good solution.
$("#next").click(next); would work. Notice parenthesis are not required as the function/callback handler should be passed as a parameter.
Are Javascript callbacks just anonymous functions sent as an argument in a function call?
For example,
mySandwich('ham', 'cheese', function() {
alert('Finished eating my sandwich.');
});
JavaScript callbacks are functions passed as values into an asynchronous function for the purpose of continuation.
Functions are values:
So in JavaScript, you can pass a functions around like values. You can reference a function in a number of ways:
Pass a literal anonymous function as the callback
doSomeWork(function (err, result) {
if (err) {
throw new Error(err);
} else {
console.log(result);
}
});
Pass a literal named function as the callback
doSomeWork(function magicalCallback(err, result) {
if (err) {
throw new Error(err);
} else {
console.log(result);
}
});
(Naming every function is a smart idea because you can see it in the stack trace)
Pass in the value of a variable which happens to be storing a function as the callback
var someFunction = function callItWhatYouWant(err, result) {
if (err) {
throw new Error(err);
} else {
console.log(result);
}
}
// reference the callback stored in the someFunction variable
doSomeWork(someFunction);
Pass in the function by referencing the function name as the callback
function callItWhatYouWant(err, result) {
if (err) {
throw new Error(err);
} else {
console.log(result);
}
}
// reference the callback function using the function name
doSomeWork(callItWhatYouWant);
Continuation?
Continuation is all about the next step. When you call a function which is asynchronous, it needs to notify you that it is done. The callback acts as the next step, i.e. the asynchronous function will call you back when it is done.
So a callback is just a function argument used for a particular purpose, that being, continuation.
Callback signature
There is no standard for which arguments a callback should take, but in the Node.js community we have adopted the general signature
function (err, result)
where err is an Error object if something bad happened, or null if things were successful. If things went bad result is generally undefined, otherwise it contains the result. So your callback is generally called by either
callback(new Error("oops"));
or
callback(null, result);
Also note that it's normal for the last parameter of an asynchronous function to be the callback parameter
function callLater(delay, args, callback) {
setTimeout(function () {
callback(null, args);
}, delay);
}
In your example: yes
But in m example: No
function myCallback()
{
alert('finished eating my sandwich');
}
mySandwich('ham','cheese', myCallback);
So, from you comment I think the real question is: What is an anonymous function? I did my best, but it is still early in the morning here, so don't shoot me.
Well, gooed question. Hard answer
when defining a function, it lives withing its scope. Scope? huh? Ok, let's start again.
when a webpage is loaded, the browser creates a window object. It then starts parsing everything you wrote in that document (let's assume HTML).
Every DOMElement it encounters, gets put into the window.document object. Every Element inside the widnow.document element is rendered/interpreted in your browser window.
Now, the browser encounters the following <script>:
var myVariable = 'So cool';
The browser sees var myVariable. This tells him to create a variable called myVariable in the current scope (again that word). the current scope is window (the window object the browser created). So it adds the variable to the window object:
window.myVariable === myVariable
The same goes for functions
function myAwesomeFunction()
{
alert('I am so cool');
}
Creates a function inside the current scope (window) with the name myAwesomeFunction. So again:
window.myAwesomeFunction === myAwesomeFunction
But what if I want to create a function I don't whant any other code to have access to? What if I want a function that should only exist when a certain specific button is clicked.
Well, enter anonymous functions. these are functions that are declared in the anonymous scope. they cannot be accessed using the window object:
myButton = document.getElementById('myButton'); // === window.document.getElementById('myButton');
myButton.addEventListener('click', function()
{
alert('I am so awesome');
});
Now, the function you passed into the eventlistener only lives inside the eventListener. It doesn't even have a name. So it's scope is 'the on click event of the button' but we can't access it from outside because it doesnt have a name. Thus, anonymous function.
A good read would be to google stuff like 'javascript scope', 'javascript window object', ... A lot of good articles give a better in depth explanation of all the words I threw at you.
Yes, except that they don't have to be anonymous functions, and there's a bit more to the definition of a callback. The other two answers have covered the details of what callbacks are for and anonymous functions well, but I have had similar confusion to yours when I was first learning so I wanted to add a missing piece. So, your example:
mySandwich('ham', 'cheese', function() {
alert('Finished eating my sandwich.');
});
I've noticed most callback explanations are like this- they don't include the declaration of the larger function, in this case mySandwich. But yes, the function this is being passed into does need to explicitly call the callback function for it to run. So your mySandwich function might look like this:
function mySandwich(meat, topping, callback) {
//some code here using meat and topping
//some code here about eating the sandwich
callback();
}
So your code passes those parameters, including the function, into the mySandwich function, which then invokes it. Because many times the top-level function we are using is a method from a library rather than part of our own code, we don't actually see the step where the callback is invoked, but it does still happen and doesn't just automatically execute itself.
So yes, a callback is really just a function being passed as a parameter to another function, and then it is invoked usually as the last thing to happen in that larger function. There's more than that to callbacks, but they operate by the same rules as other functions in that somewhere they have to be declared and somewhere they have to be called, otherwise they don't run.
How can I get this function to pass by reference with this code?
var Class = function() {
var callback1;
var callback2;
function buildStuff(data, callback) {
element.onclick = function() {
doStuff(callback);
};
}
function doStuff(callback) {
callback();
}
return {
"setCallback1":function(fn) {
callback1 = fn;
},
"setCallback2":function(fn) {
callback2 = fn;
},
//may rebuild with different data, but same callback
"buildFoo":function(data) {
buildStuff(data, callback1);
},
//may rebuild with different data, but same callback
"buildBar":function(data) {
buildStuff(data, callback2);
}
};
}
function main() {
var object = Class();
object.setCallback1(function() {
//do stuff
});
object.setCallback2(function() {
//do something else
});
}
When you actually click on the element, callback is undefined. I would expect it to be the anonymous function I set it to with the setCallback function because the user click occurs after I call the setCallback function.
Thanks!
EDIT: Thanks for the input. I should have mentioned I need to be able to dynamically set what callback equals. So, I can't just eliminate the callback parameter from buildStuff.
EDIT2: Very sorry for the confusion; I realize my example was a bit too out of context to show what I am doing. buildStuff is actually a private member function (using the module pattern) that is called repeatedly. Depending on what is being built, it needs a different callback. The callback is actually set outside of the class (well, module pattern class), so it has to be dynamic. I've updated my code, and again, sorry for the bad example.
The click handler you create in buildStuff creates a closure over the local variables. You pass callback to your buildStuff function, but at the time you pass it, it's undefined. As this shadows the other callback variable, you always see this value of undefined, rather than the state of the other callback variable.
Instead, don't pass a parameter to buildStuff, and the closure will be created, and will capture the callback variable you want.
function buildStuff() {
element.onclick = function() {
doStuff(callback);
};
}
Imagine this;
Your global variable callback points to a value (in this case undefined).
When you buildStuff in main(), you pass the value pointed to by callback (undefined) as a parameter to buildStuff
Your click handler creates a closure over local variables + other variables in scope (note that the local callback shadows the global callback). callback in your event handler is now undefined.
You then setCallback. setCallback changes the value the global callback variable points to using the = operator. The global callback and local callback now point to different values, which is why you don't see the callback in the event handler update.
What you want to do in this situation is to change the value pointed to by callback, so other variables pointing there also update, but JavaScript doesn't let you do this.
Yes, but you've already called buildStuff before setCallback.
The contents of callback at the time (undefined) will be used.
If you want to call buildStuff with different callbacks, just do that, and eliminate the redundant setCallback:
function buildStuff(callback) {
element.onclick = function() {
doStuff(callback);
};
}
function doStuff(callback) {
callback();
}
function main() {
buildStuff(
function() {
//do something
}
);
}
Hey everyone - I'm having some difficulties properly getting a return value from one of my Javascript callback functions, and it looks to be dependent on a race condition, but I'm not sure:
JSOBJ.container = function() {
return {
getName: function() {
var value;
companyfn.app.getInfo(callback);
function callback(foo) {
// alert("gets here");
if (foo.hadError()) {
alert("Error found!");
} else {
value = foo.getField(companyfn.app.Field.SUB_DOMAIN);
}
// alert('callback: ' + value);
}
return value;
}
}
}();
JSOBJ.main = function () {
return {
init: function() {
alert(JSOBJ.container.getName());
}
};
}();
In JSOBJ.main.init(); above, I'm trying to get the proper value, but when I run my code, I almost always get a return value of undefined. When I uncomment my alert statements in JSOBJ.container.getName(), the getName function seems to run without invoking the callback, the alert pops up, and then the getName function gets called. So it feels like a race condition, and I want to say it has to do with closures, but I'm not sure how to implement it properly so it "waits" for getField to return a value. Can anyone help?
Pass the defined function to your ajax call (I am assuming that is an ajax call) or pass it as an anonymous lambda. Your callback function is created on the fly after the call that passes it.
You cannot make your function block until the callback finishes without completely freezing the browser, which is not a good idea.
You need to make your getName function take a callback and give the callback its value, just like companyfn.app.getInfo.