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
}
);
}
Related
Given an <input id="foo"> element, I want to call an existing function on blur and pass an anonymous callback to it.
Case 1, simple function call:
function bar(){
alert("I am");
}
$("#foo").blur(bar); //Works fine
Case 2, passing arguments/function:
$("#foo").bind("blur", {func: function(){console.log("I am apple")}}, bar);
function bar(event){
event.data.func(); //Works fine
};
Problem with case 2 is that I'd like to have bar() as a generic function called by more than blur, in which case I would be passing anonymous function directly, not "baked" into a data object. So ideally like this:
function bar(callback){
//Do stuff
callback();
}
bar(function(){console.log("Hello Earth")}); //Works
$("#foo").blur(bar(function(){console.log("Hello world")})); //Doesn't work
Last line doesn't work as function gets executed directly, but it's an example of what I would like to achieve. I guess I possibly could make some typeof checks in bar() to determine what I am receiving, but I wanted to ask if there's a cleaner way to pass anonymous function on blur, instead of changing bar().
...I wanted to ask if there's a cleaner way to pass anonymous function on blur, instead of changing bar().
Not sure quite what you mean by that, you will have to either change bar or wrap bar.
Changing it:
Make bar a handler generator rather than a direct handler:
function bar(callback) {
return function(e) {
// You could use `e` (the event) here and/or pass it on to the callback
callback();
// You might use the callback's return, e.g.:
// return callback();
// ...if you wanted it to be able to `return false`
};
}
Now, bar returns an event handler that will call the callback (in addition, presumably, to doing something of its own; otherwise, it's a bit pointless and you'd just pass the callback directly).
Usage:
$("#foo").blur(bar(function(){console.log("Hello world")}));
Wrapping it
If you literally mean you don't want to change bar, you'll need to wrap it:
function wrap(f, callback) {
return function() {
// You could call `callback` here instead of later if you like
// Call the original function passing along the `this` and
// arguments we received (`arguments` isn't pseudo-code, it's
// a pseudo-array JavaScript creates for you), get its return value
var retval = f.apply(this, arguments);
// Call the callback
callback();
// Return the original function's return value
return retval;
};
}
If you want to avoid letting errors thrown by callback to make it to the event mechanism, you can wrap it in a try/catch.
Usage:
$("#foo").blur(wrap(bar, function(){console.log("Hello world")}));
function bar(callback){
//Do stuff
callback();
}
bar(function(){console.log("Hello Earth")}); //Works
$("#foo").blur(bar.bind(this,function(){console.log("Hello world")})); //Will work
Wrap it and return it as an anonymous function:
function bar(callback){
return function() {
callback();
}
}
// direct
bar(function(){console.log("Hello Earth")})();
// as callback
$("#foo").blur(bar(function(){console.log("Hello world")}));
function bar(){
alert("I am blur");
}
$("#foo").bind('blur',bar);
Last line doesn't work as function gets executed directly
$("#foo").blur(bar(function(){console.log("Hello world")}));
On the last line you're passing the result of bar to the blur() function, not bar itself, it's the same as:
var result = bar(function(){console.log("Hello world")});
$("#foo").blur(result);
The standard method is to wrap this in an anon function:
$("#foo").blur(function() { bar(function(){console.log("Hello world")}) });
This means there are no changes to your other existing functions, as per the question requirement: "instead of changing bar()"
I'm brushing up on callback functions and came across the following passage from http://javascriptissexy.com/understand-javascript-callback-functions-and-use-them/#
"When we pass a callback function as an argument to another function, we are only passing the function definition. We are not executing the function in the parameter. In other words, we aren’t passing the function with the trailing pair of executing parenthesis () like we do when we are executing a function.
And since the containing function has the callback function in its parameter as a function definition, it can execute the callback anytime."
Can someone explain that? Here are two examples they provided.
//The item is a callback function
$("#btn_1").click(function() {
alert("Btn 1 Clicked");
});
Here is another example:
var friends = ["Mike", "Stacy", "Andy", "Rick"];
friends.forEach(function (eachName, index){
console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick
});
"Note that the callback function is not executed immediately. It is “called back” (hence the name) at some specified point inside the containing function’s body. So, even though the first jQuery example looked like this:
//The anonymous function is not being executed there in the parameter.
//The item is a callback function
$("#btn_1").click(function() {
alert("Btn 1 Clicked");
});
the anonymous function will be called later inside the function body. Even without a name, it can still be accessed later via the arguments object by the containing function."
For the first example with jquery, what are they saying exactly. If the #btn_1 element is clicked, will the anonymous function be executed? I am assuming it will be executed if the button is clicked, but the wording from the passage was confusing?
Similarly, for the second example, do they not need to call the function that they passed as an argument bc its anonymous?
In both examples, you are passing an anonymous function as a parameter.
$("#btn_1").click(function() {
alert("Btn 1 Clicked");
});
jQuery's click method takes a function as its first parameter. So imagine that click's function definition is this:
function click(fn) {
// fn will contain a reference to any
// function passed as the first parameter to click
// merely calling fn does nothing, because you are just 'calling'
// the reference.
fn;
// Since what is inside of fn is a function, you can execute it
// with the () syntax
fn();
}
// Now, you have many ways to pass a function as the first parameter to the function
// 1. As an anonymous function:
click(function() {
console.log("Hi");
});
// 2. As a named function:
click(function hello() {
console.log("Hi");
});
// 3. As a reference to a function declaration
function hiThere() {
console.log("Hi");
}
click(hiThere);
// 4. As a variable that holds an anonymous function inside
var howdy = function () {
console.log("howdy");
};
click(howdy);
Just imagine that functions are like variables, but they have content inside that can be executed with () at the end.
function hi() {
console.log('bye');
}
hi; // Calls the reference, but does not execute it. This does nothing.
hi.toString(); // Returns the function as a string
hi(); // Executes the code within the function
Whenever you declare a named function, you can do stuff with it according to its name, like you would do with variables. Of course, unlike variables, they hold executable code inside, and not values.
You can't reference an anonymous function, because it's well... anonymous. UNLESS, you hold it inside of something that has a name, like a var.
var iHoldAFunctionInside = function () {
console.log('Im not so anonymous now');
};
iHoldAFunctionInside(); // Logs "Im not so anonymous now"
And that is why you can pass an anonymous function as a parameter to a function, and it can execute it as a callback. Because the parameter now 'holds' the anonymous function inside of it:
function iExecuteYourCallback(callback) {
// callback contains the anonymous function passed to it
// Similar to doing:
// var callback = function () { };
callback();
}
iExecuteYourCallback(function() {
console.log('Im a callback function!');
});
Hope this helps clear things a bit.
In javascript functions are first class members to you can pass a function as an parameter and the called function can accept it as a named argument.
A simple example can be as below
function testme(callback) {
//here the argument callback refers to the passed function
//the timer is used just to delay the execution of the callback
setTimeout(function () {
//the passed function is called here
callback();
}, 1000)
}
testme(function () {
alert('x')
})
Demo: Fiddle
In your examples, yes the first callback will be executed once the element with id btn_1 is clicked.
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.
Consider the following jQuery implementation defined using an object literal...
$(function() {
var myObject = {
methodOne: function()
{
$('#element').animate(
{'marginLeft': '50px'},
'slow',
function() {
myObject.methodTwo();
}
);
},
methodTwo: function()
{
$('#element').animate(
{'marginLeft': '-50px'},
'slow',
function() {
myObject.methodOne();
}
);
}
} // End myObject
myObject.methodOne(); // Execute
});
For the record, the above code works just as expected. What I don't understand is why a subtle and seemingly harmless change like the following...
methodOne: function()
{
$('#element').animate(
{'marginLeft': '50px'},
'slow',
myObject.methodTwo() // No more anonymous function
);
},
... to both methodOne and methodTwo causes a browser error stating too much recursion. What's the difference between how I've declared my callback? Also, if I bring back the anonymous function declaration, but modify the object reference to look like this...
methodOne: function()
{
$('#element').animate(
{'marginLeft': '50px'},
'slow',
function() {
this.methodTwo(); // assuming 'this' refers to 'myObject'
}
);
},
... I get one good pass through methodOne and upon callback my browser freaks out because it cannot find methodTwo. My guess is that I fell out of scope somewhere, but I can't rightly decide where. Your insight is much appreciated!
Lets break this down
$('#element').animate(
{'marginLeft': '50px'},
'slow',
function() {
myObject.methodTwo();
}
);
In this example you pass a function object as a callback. That function object is called when animation is done. It has the myObject object shared via closure, so it can easily find it and call a method on it. Awesome!
$('#element').animate(
{'marginLeft': '50px'},
'slow',
myObject.methodTwo() // No more anonymous function
);
Here something different is going on. As the callback here you are actually passing the return value of myObject.methodTwo(), and not the actual function object. So since methodTwo() doesn't return anything, then undefined is actually passed as your callback. Meaning that the animate() function thinks there is no callback.
So maybe you meant to try this!
$('#element').animate(
{'marginLeft': '50px'},
'slow',
myObject.methodTwo // No more anonymous function or invocation
);
Well this still wouldn't work. Now you are passing a function object for the callback, yes, but it will lose context (this). It turns out that when you invoke a function object on it's own, then this is the global object. Check this out:
var obj = {
foo: function() { console.log(this) }
};
obj.foo() // logs obj
var func = obj.foo;
func() // logs window (the global object in a browser)
So you cant pass a function object directly in for a callback that is supposed to be invoked like a method with an object as the receiver. Internally to the animate() method there, it executes callback() for you, which is a call that does not preserve the value of this for you at all.
So why didnt this one work?
$('#element').animate(
{'marginLeft': '50px'},
'slow',
function() {
this.methodTwo(); // assuming 'this' refers to 'myObject'
}
);
When an anonymous function is invoke as the callback, just like breaking off a method, this defaults to the window object. So this code actually calls window.methodTwo(), which doesn't exist, and it explodes.
So the accepted standard JS way to do this your first way.
someFunc(arg1, arg2, function() {
someObj.someMethod()
});
That should always work, even though it seems wasted because you invoking 2 function to do one thing. But as you are discovering it's the least error prone.
Learning how this works in JS is a painful experience, but when you get it, you find that the rules are pretty straight forward and easy to manipulate.
If you still dont like that, you can do some tricky js magic. Like underscore.js bind() method.
someFunc(arg1, arg2, _.bind(someObj.someMethod, someObj));
This returns a function that will always run with someObj as this which can safely be used as a callback.
Or if you want to try out CoffeeScript it has a fat arrow to preserve context, wich compiles to JS similar to what the underscore.js bind() method does.
someObj =
foo: ->
someFunc arg1, arg2, =>
this.bar()
bar: ->
alert 'got the callback!'
someObj.foo()
In this case, the => style of function declaration preserves the context of the scope where it appears, so this can be safely used.
when you give jquery the function to execute you are actually passing in a function pointer and not what you want to execute.. it basically does exec on the function reference that you passed and thus when you changed it to obj.method() it longer understood it..
The problem with the myObject.methodTwo() is that myObject.methodTwo() is evaluated before being passed as a function. Javascript and by extension jQuery expects a function pointer in cases like these. I.E. setTimeout function.
In the below usage, an anonymous function is created (just a block of statements) and is registered as the callback function. Note the use of 'function(){'. The anonymous function does exactly one thing: calls myObject.methodOne()
function() {
myObject.methodOne();
}
regarding you second question in the callback function the this refers to a different object and not your original object anymore and that is why that failed. if you want to pass your this reference to the callback function read this: http://thomasdavis.github.com/tutorial/anonymous-functions.html
When you put the callback as myObject.methodTwo(), you are actually invoking methodTwo then and there. Then inside methodTwo it does the same thing by calling methodOne...which calls methodTwo, which calls methodOne, and so on. So you have an infinite recursion.
When you wrap that same line of code inside an anonymous function, it does not invoke the function immediately. You are now passing the function itself, not the returned value. The function using the callback then determines when (if ever) to actually invoke the function.
As zzzz and Paul stated, you're calling the function and passing its return value, rather than passing a callback. But you don't need to wrap that in an anonymous function to make it work - simply remove the () after the method and it'll work fine.
This.methodTwo here is looking for local reference, This belong to current scope, so method two is define out side the current scope you can use . I just quick view the code and find the following suggestion.
methodOne: function()
{
var that = this;
$('#element').animate(
{'marginLeft': '50px'},
'slow',
function() {
that.methodTwo(); // assuming 'this' refers to 'myObject'
}
);
}
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.