I want to attach a callback method to a javascript function in order to be called after its execution
Thanks in advance
When you can't avoid doing this (which is usually best), the usual way to do it is to wrap the function in another function:
function f(msg) {
console.log("The message is: " + msg);
}
f("Before being wrapped");
console.log("Now wrapping the function");
// Then wrapping it
var originalF = f;
f = function() {
var returnValue;
console.log("This is before the original function");
returnValue = originalF.apply(this, arguments);
console.log("This is after the original function");
return returnValue;
};
f("After being wrapped");
Note the use of Function#apply to ensure that we call the original function with the same this value we were called with and all of the original arguments. Then we keep the return value and return it when we're done.
Of course, if the original function started an asynchronous task, we've only wrapped the original function, not the code triggered when the task is complete.
You need to redefine the function. You can do this with an IIFE.
funcname = (function(oldfunc, callback) {
return function() {
var value = oldfunc.apply(this, arguments);
callback();
return value;
})(funcname, callback);
);
Related
i was reading the source code for async.js library and with in that i found a function called 'only_once' and then i tried some examples to make it clear how it works, but i can't figure out what is wrong with my examples cause they simply do not behave the way they should. here is my code:
function only_once(fn) {
var called = false;
return function () {
if (called) throw new Error('Callback was already called.');
called = true;
fn.apply(this, arguments);
};
}
// my example code
var add = function (a, b) { return a + b; };
var add_once = only_once(add);
var a = add_once(1, 3);
console.log(a); // this prints undefined not 4, what the hell ?????
only_once() isn't currently doing anything with the result of fn -- add, in this case.
It should provide the return value from fn as its own:
return fn.apply(this, arguments);
// vs.
fn.apply(this, arguments);
In context:
// ...
return function () {
if (called) throw new Error('Callback was already called.');
called = true;
return fn.apply(this, arguments);
};
// ...
Within async.js, the return value isn't being used, so it doesn't have to be provided.
The asynchronous functions the library is designed for instead accept callback functions as arguments to be invoked later and passed the results.
only_once is just preventing these from being invoked multiple times:
function asyncFunc(callback) {
setTimeout(callback, 100); // callback invoked
setTimeout(callback, 200); // Uncaught Error: Callback was already called.
}
asyncFunc(only_once(function (value) {
console.log('callback invoked');
}));
I have this spec from Jasmine.js which tests a once function. I'm not sure how to implement such a function though.
/* Functions that decorate other functions. These functions return a version of the function
with some changed behavior. */
// Given a function, return a new function will only run once, no matter how many times it's called
describe("once", function() {
it("should only increment num one time", function() {
var num = 0;
var increment = once(function() {
num++;
});
increment();
increment();
expect(num).toEqual(1);
});
});
I don't quite understand what should I do here. I know I should make a function once(myFunction) {} but other than that, I am stuck. I figure out this has something to do with closures, still can't my head around it.
If you prefer not to use UnderscoreJS, you can implement a simpler "once" function yourself like this:
var once = function (func) {
var result;
return function () {
if (func) {
result = func.apply(this, arguments);
func = null;
}
return result;
}
};
When you pass your function as the argument to this once function (as the parameter as 'func'), it returns a function that can only be called once.
It accomplishes this feat, in short, by creating a results variable and assigning that variable the results of calling your function with its supplied arguments--but only the first time it is run. Otherwise, when the function is invoked subsequent times, it will never enter your if statement (because the func variable was set to null in the first invocation) and the value referenced by the results variable (set during the first invocation and accessed via closure) will be returned.
Copied from the UnderscoreJS source:
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
http://underscorejs.org/docs/underscore.html
Very, very minimal
const once = fn => (...args) => {
if (!fn) return;
fn(...args);
fn = null;
};
(Old school version)
function once(fn) {
return function() {
if (!fn) return;
fn.apply(null, arguments);
fn = null;
}
}
I intend to create a "preprocessual" function that is invoked right before a callback is invoked. In other words, invoking a callback should follow the pattern: preprocessual function -> callback. In order to "insert" such a preprocessual function, I could simply create a closure, rewrite the callback inside the closure so that the preprocessual function gets invoked, then at the end of that rewritten callback, invoke the original callback.
var end = function(init) {
/*
In here, init is processed.
Init contains multiple callbacks.
One callback is chosen to be invoked.
*/
init.callback();
};
var closure = function(init) {
var old = init.callback;
init.callback = function() {
/*
Do the preprocessual stuff
*/
console.log("The preprocessual functionality has now taken place.");
return old.apply(this, Array.prototype.slice.call(arguments));
};
return end.apply(this, Array.prototype.slice.call(arguments));
};
closure({
/*among other properties*/
callback: function() {
console.log("The preprocessual callback must have been invoked when 'end' invokes me.");
}
});
However, I have multiple callbacks, while I have only one preprocessual function. Each invocation of those callbacks should be preceded by an invocation of the same preprocessual function. In order to not have to write a preprocessual callback for each separate possible callback, I made a loop in the closure, that assigns the variable old to the next callback, then rewrote the callback using the Function constructor.
Everything still works. However, I am no longer able to use non global variables in my callback function that it could originally access. The following crashes, claiming that variable is not defined (as per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function).
(function() {
var end = function(init) {
/*
In here, init is processed.
Init contains multiple callbacks.
One callback is chosen to be invoked.
*/
init.callback();
};
var closure = function(init) {
var old = init.callback;
init.callback = new Function(
"\
/*\
Do the preprocessual stuff\
*/\
console.log(\"The preprocessual functionality has now taken place.\");\
return " + old + ".apply(this, Array.prototype.slice.call(arguments));\
"
);
return end.apply(this, Array.prototype.slice.call(arguments));
};
var variable = "value";
closure({
/*among other properties*/
callback: function() {
console.log("The preprocessual callback must have been invoked when 'end' invokes me.");
console.log(variable);
}
});
})();
So then I thought, let's try to bind the variables I need in my callback to the callback function. I then encountered a very strange problem. For some reason binding a scope/parameters to the callback function (with which the Function constructor has little to do), results in strange errors. A small example of such an error:
This works
var callback = function() {
console.log(arguments);
};
callback = new Function(
"\
return " + callback + ".apply(this, Array.prototype.slice.call(arguments));\
"
);
callback(1, 2, 3);
This does not work
var callback = function() {
console.log(arguments);
}.bind(this);
callback = new Function(
"\
return " + callback + ".apply(this, Array.prototype.slice.call(arguments));\
"
);
callback(1, 2, 3);
It does not matter if I assign the callback to another variable, such as old, in between and use old in the Function constructor and it doesn't matter if I use a completely different bound function inside the Function constructor, either. Any bound function (whether referenced to with a variable or not) gives me the error: "SyntaxError: missing ] after element list".
In fact, even this fails
callback = new Function(
"\
return " + (function() {}.bind(this)) + ".apply(this, Array.prototype.slice.call(arguments));\
"
);
callback(1, 2, 3);
And I fail to figure out why this is the case. Useful help would be appreciated.
As requested, the actual use case:
var
ajax = function(init) {
for (var i = 0, callbacks = ["success", "error"]; i < callbacks.length; i++) {
if (init.hasOwnProperty(callbacks[i] + "Callback")) {
init[callbacks[i] + "Callback"] = new Function("responseText",
"\
/*\
Preprocessual callback takes place here (among other things, messages from the server are inserted in the document)\
*/\
\
return " + init[callbacks[i] + "Callback"] + ".apply(this, Array.prototype.slice.call(arguments));\
"
);
}
}
// This is the actual ajax function, which can operate independently of the project (in contrary, the preprocessual callback needs to know about where to insert messages in the document)
return cregora.ajax.apply(this, Array.prototype.slice.call(arguments));
}
;
(function() {
// some scope with variables..
ajax({
url: "url",
callbackThis: this,
successCallback: function(responseText) {
console.log("I need some variables available in this scope");
},
errorCallback: function() {
console.log("I need some variables available in this scope");
}
});
})();
As I expected, you were actually overcomplicating the issue a little bit.
Instead of using the function constructor, you can build a higher order function that returns an appropriate handler and automatically wraps the function (like your preprocessors).
var callbackWrapper = function (callback) {
// Returns new anonymous function that acts as the handler
return function responseHandler (responseText) {
// Do your pre-processing
console.log(responseText);
callback.apply(this, Array.prototype.slice.call(arguments));
};
};
var ajax = function(init) {
for (var i = 0, callbacks = ["success", "error"]; i < callbacks.length; i++) {
var callbackName = callbacks[i] + "Callback";
if (init.hasOwnProperty(callbackName)) {
var callback = init[callbackName];
init[callbackName] = callbackWrapper(callback);
}
}
// This is the actual ajax function, which can operate in independent of the project (for example, the preprocessual callback needs to know about where to insert messages in the document)
return cregora.ajax.apply(this, Array.prototype.slice.call(arguments));
};
(function() {
// some scope with variables..
ajax({
url: "url",
callbackThis: this,
successCallback: function(responseText) {
console.log("I need some variables available in this scope");
},
errorCallback: function() {
console.log("I need some variables available in this scope");
}
});
})();
If you care, you can even change the callbackWrapper to use exactly the same preProcessor function every time:
var callbackWrapper = (function createCallbackWrapper () {
var preProcessor = function (responseText) {
console.log(responseText);
};
return function callbackWrapper (callback) {
// Returns new anonymous function that acts as the handler
return function responseHandler (responseText) {
var args = Array.prototype.slice.call(arguments);
preProcessor.apply(this, args);
callback.apply(this, args);
};
};
})();
Now you will have no problems at all with binding the original callback functions.
A little more explanation on the issue:
When you use fn + ".apply(...)", JS will turn the original function into a string. That is why you will have a hard time accessing closure variables, or anything else that is not in either your var closure function scope or the global scope.
It also fails in your case because after calling .bind on a function, its string representation turns into "function () { [native code] }".
That is of course not a valid function body and will give you lots of trouble.
That conversion to a string is the actual problem, and it is one that is not easily solved. For that reason, using new Function is almost never the proper solution, and once you found yourself using it, you should assume you made a mistake in your reasoning. If you didn't, and new Function is indeed the only solution, you'd know.
I've this situation:
<script>
var cb;
function doSomething(c) {
cb = c();
}
cb();
</script>
But it doesn't work. I want to set a variable as function, to make a callback called by other functions.. Some ideas?
c() executes the function and returns a value, you need to pass a reference to it:
cb = c
Also, you should call the function doSomething(func) to make the assignment.
doSomething(function(){ alert('hello'); });
cb(); // "Hello"
But if what you want is a callback then you don't need a global variable:
function doSomething(callback) {
// do something
if (callback) callback();
}
When you run the function with another function as parameter the callback will run.
You need to assign cb first with your function:
<script>
var cb;
function doSomething(c) {
cb = c;
}
var myFunc = function(){
window.alert("test");
}
doSomething(myFunc);
cb();
</script>
And if you do cb=c(); you will execute function c instantly and return the value to cb if you want the variable cb as the result of c, do like this. otherwise, assign without running it.
You have 3 options to set a variable to a functiion
var fn = functionname;
var fn = function(param){}; this will be an anonymous function
var fn = function FunctionName(param){}; this will be a named function, comes in handy when debugging, since it will present you with a function name (console.log(c);
You cann call it like var returnVal = fn(); or pass it to a function var returnVal = myFunc(fn); where myFunc calls the param, let it be inFn it like inFn();
What might be interesting to note:
Since such a function is related to the global context, you can bind an object to it alter its scope. That gives you the possibility of thisreferencing the bound object. (Be aware, bind is not supported by all browsers as it is defined ECMAScript 5, but there are quite some polyfills out there.)
fn.bind(object);
Or call it in another context with fn.call(object, param1, param2) or fn.apply(object, [param1, param2]). Nice write up on this Link to odetocode.com/blog.
I have a javascript function (class) that takes a function reference as one paremter.
function MyClass ( callBack ) {
if (typeof callBack !== 'function')
throw "You didn't pass me a function!"
}
For reasons I won't go in to here, I need to append something to the function by enclosing it in an anonymous function, but the only way I've been able to figure out how to do it is by adding a public function to MyClass that takes the callBack function as a parameter and returns the modified version.
function MyClass () {
this.modifyCallBack = function ( callBack ) {
var oldCallBack = callBack;
callBack = function () {
oldCallBack(); // call the original functionality
/* new code goes here */
}
return callBack;
}
}
/* elsewhere on the page, after the class is instantiated and the callback function defined */
myCallBackFunction = MyClassInstance.modifyCallBack( myCallBackFunction );
Is it possible to make this work when passing the callBack function as a parameter to the class? Attempting to modify the function in this manner when passign it as a parameter seems to only affect the instance of it in within the class, but that doesn't seem like it's a valid assumption since functions are Objects in javascript, and are hence passed by reference.
Update: as crescentfresh pointed out (and I failed to explain well), I want to modify the callBack function in-place. I'd rather not call a second function if it's possible to do all of this when the class is instantiated.
Function objects don't provide methods to modify them. Therefore, what you want to do is impossible the way you want to do it. It's the same thing Jon Skeet likes to point out about Java: Objects are not really passed by reference, but instead a pointer to them is passed by value. That means that changing the value of an argument variable to a new one won't affect the original one at all.
There are only two ways to do what you want in call-by-value languages like Java and JavaScript: The first one would be to use the (function) object's methods to modify it. As I already stated, function objects don't have those. The other one is to pass the object of which the function object is a property as a second argument and set the appropriate property to a new function which wraps the old one.
Example:
var foo = {};
foo.func = function() {};
function wrapFunc(obj) {
var oldFunc = obj.func;
obj.func = function() {
// do some stuff
oldFunc.call(obj, _some_argument__);
};
}
wrapFunc(foo);
This works for global functions as well: they are properties of the window object.
As Javascript uses lexical scoping on variables the following is possible:
var modifiableCallback=function() { alert('A'); };
function ModifyCallbackClass(callback)
{
modifiableCallback=function() { callback(); alert('B'); };
}
function body_onload()
{
var myClass=new ModifyCallbackClass(modifiableCallback);
modifiableCallback();
}
This does what you want, however the function "modifiableCallback" must be referred to with the same name inside ModifyCallbackClass, otherwise the closure will not be applied. So this may limit the usefulness of this approach for you a little.
Using eval (performance may suffer a bit) it is also possible to make this approach more flexible:
var modfiableCallback1=function() { alert('A'); };
var modfiableCallback2=function() { alert('B'); };
var modfiableCallback3=function() { alert('C'); };
function ModifyCallbackClass(callbackName)
{
var temp=eval(callbackName);
var temp2=eval(callbackName);
temp= function() { temp2(); alert('Modified'); };
eval(callbackName + " = temp;");
}
function body_onload()
{
var myClass=new ModifyCallbackClass("modfiableCallback1");
modfiableCallback1();
myClass=new ModifyCallbackClass("modfiableCallback2");
modfiableCallback2();
myClass=new ModifyCallbackClass("modfiableCallback3");
modfiableCallback3();
}
I assume you are saving this callback somewhere... Any reason this won't work?
function MyClass ( callBack ) {
var myCallBack;
if (typeof callBack !== 'function')
throw "You didn't pass me a function!"
var oldCallBack = callBack;
callBack = function () {
oldCallBack(); // call the original functionality
/* new code goes here */
}
myCallBack = callback;
}
You want to do something like:
function MyClass () {
this.modifyCallBack = function ( callBack ) {
var oldCallBack = callBack;
callBack = function () {
oldCallBack(); // call the original functionality
alert("new functionality");
}
return callBack;
}
}
/* elsewhere on the page, after the class is instantiated and the callback function defined */
var myCallBackFunction = function () {alert("original");};
var MyClassInstance = new MyClass();
myCallBackFunction = MyClassInstance.modifyCallBack( myCallBackFunction );
myCallBackFunction();