How can I callback to a function in an object?
json_post('get_tracks', 'json.request.php?get=tracks', 'genreId='+id+'&perPage=70&page=1', 'rtn_tracks');
Instead of making a callback to rtn_tracks() I want to do it to this.rtn()
How can I define this in the callback string?
Here is the code:
function stream_tracks(){
this.get = function(id){
json_post('get_tracks', 'json.request.php?get=tracks', 'genreId='+id+'&perPage=70&page=1', 'rtn_tracks');
};
this.rtn = function(json_obj){
this.cnstr(json_obj);
};
this.cnstr = function(json_obj){
alert('test');
};
}
Stream_tracks = new stream_tracks();
var XMLHTTP = {};
function json_post(request_uid, uri, get_str, callback_function, callback_var){
request_uid += Math.floor(Math.random()*999999).toString();
if(window.XMLHttpRequest){
XMLHTTP[request_uid] = new XMLHttpRequest();
}
else if(window.ActiveXObject){
XMLHTTP[request_uid] = new ActiveXObject('Microsoft.XMLHTTP');
}
XMLHTTP[request_uid].open('POST', uri, true);
XMLHTTP[request_uid].setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
XMLHTTP[request_uid].onreadystatechange = function(){
if(XMLHTTP[request_uid].readyState == 4){
if(callback_function){
eval(callback_function+'('+XMLHTTP[request_uid].responseText+(callback_var ? ', callback_var':'')+')');
}
}
}
XMLHTTP[request_uid].send(get_str);
}
Instead of using a string for callback, use a method.
var my = {
start : function (s, callback) {
callback(s);
},
callback: function(s) {
}
}
You cannot use:
my.start("Hello World", my.callback)
Since this will cause the method to be processed without connection to the object my but you can do this.
my.start("Hello World", function(s) { my.callback(s); });
You can pass functions as objects in Javascript, you don't need to pass the function name and use eval. If your callback should be called as a member of a class, you need to pass along the class instance as well. E.g. add a callback context argument to your json function:
function json_post(request_uid, uri, get_str, callback_function, callback_var, callback_context){
/*... snip ...*/
XMLHTTP[request_uid].onreadystatechange = function(){
if(XMLHTTP[request_uid].readyState == 4)
{
if(callback_function){
/* The call() function lets you call a function in a context */
callback_function.call(
callback_context || this,
XMLHTTP[request_uid].responseText,
callback_var
);
}
}
};
XMLHTTP[request_uid].send(get_str);
}
Then you would call it like so:
json_post('get_tracks', 'json.request.php?get=tracks', 'genreId='+id+'&perPage=70&page=1',
this.rtn, // callback function
null, // callback var
this // callback context
);
Here's a good tip though: Use a framework! It will make your day a lot easier.
Ok so there are a couple of things that you need to do and it might make more sense if you have a read about closures.
Firstly you'll need to make a reference to the this variable so you can access it inside your callback without overwritting it.
function stream_tracks(){
var obj = this;
Then if you want to refer to properties of that class/object from within its other methods you just use obj.this.
The second thing you should do is pass the callback as a function not as a string. It will also be more efficient as you will be able to do away with the eval function.
this.get = function(id){
json_post(
'get_tracks',
'json.request.php?get=tracks',
'genreId='+id+'&perPage=70&page=1',
function(){ obj.rtn(); }
);
};
Wrapping the callback in the anonymous function forms the closure and allows the function to use the variables from class. Also if you do it this way you can pass any parameters through at the same time as the function and do away with the extra parameters in the parent function.
Related
I am using a few callbacks in an app that I'm writing. I am using Mongoose models and need to save a few different places. The save function takes a callback, and the callback gets error and model for its parameters, but I'd like to send the callback an extra parameter that the function needs. I'm not sure of the proper syntax to be able to do this. Below is some example code of what I'm going for...
var saveCallBack = function(err, model, email_address){
if(err) {
//handle error
}
else {
//use the third parameter, email_address, to do something useful
}
};
Below, token is a mongoose model. As I said, save takes a callback and gets passed error and model, but I'd like to also send my callback a variable email_address that I figure out at some other point. Obviously the appendParameter function is pseudo-code, but this is the type of functionality that I need.
token.save(saveCallBack.appendParameter(email_address));
If you make that the first parameter instead, you can use .bind().
token.save(saveCallBack.bind(null, email_address));
var saveCallBack = function(email_address, err, model){};
I'm using bind function for appending additional parameters for callbackes
var customBind = function (fn, scope, args, appendArgs) {
if (arguments.length === 2) {
return function () {
return fn.apply(scope, arguments);
};
}
var method = fn,
slice = Array.prototype.slice;
return function () {
var callArgs = args || arguments;
if (appendArgs === true) {
callArgs = slice.call(arguments, 0);
callArgs = callArgs.concat(args);
} else if (typeof appendArgs == 'number') {
callArgs = slice.call(arguments, 0);
}
return method.apply(scope || window, callArgs);
};
}
This customBind function accepts four arguments, first one is original callback function, second is the scope, third is additional parameters (array), and fourth is flag append or replace. If you set last parameter to false than only parameters in array will be available in this function.
and with this function you can simple add new parameters or to override the existing one
var callback = customBind(saveCallBack, this, [array_of_additional_params], true)
in this way all original parameters remain and your parameter will be appended to the end.
No matter how many parameter you defined, the callee will always pass the same parameter inside its process.
but it will be more simple, just use a variable that is visible from outside of the callback.
Eg:
var email = 'yourmail#mail.com';
var saveCallBack = function(err, model){
if(err) {
//handle error
}
else {
alert(email);
}
};
Updated (#Jason): then you can use Immediately-Invoked Function Expression (IIFE)
(function(mail){
var saveCallBack = function(err, model){
if(err) {
//handle error
}
else {
alert(mail);
}
};
token.save(saveCallBack);
}, emailAddress);
I need for a function to be executable only after an object is defined, I'm currently working in a fascade pattern and one method is dependent on another method. in this case 'addNewLayer' fails because 'setFullMap' hasn't finished executing. is there a solution? I'm using jquery and vanilla js so most any solution would be helpful at this point:
var jen = (function(){
function setFullMap(mapID){
jen.map = new Map(mapID);
}
function setLayer(opt){
//execute code here after jen.map is defined
}
return{
samp: function(id, opt){
setFullMap(id);
addNewLayer(opt);
}
};
})();
Thanks
solution:
var jen = (function(){
function setFullMap(mapID, callback) {
jen.map = new Map(mapID);
if(jen.map){
callback();
}
}
return {
samp: function(id, opt){
setFullMap(id, function(){
addNewLayer(opt);
}.bind(this));
}
};
})();
You will have to pass a callback function to setFullMap, and execute it once the function has completed (at the very end, before the closing }).
var jen = (function(){
function setFullMap(mapID, callback){
jen.map = new Map(mapID);
callback();
}
function setLayer(opt){
//execute code here after jen.map is defined
}
return{
samp: function(id, opt){
setFullMap(id, function() {
addNewLayer(opt);
}.bind(this));
}
};
})();
Do not forget using .bind(this) - it is very important in order to keep the original this in your callback function.
Edit:
Actually that would not work work if the Map constructor is a-synchronous. If you do not have access to the constructor and/or you cannot pass it a callback, then presumably the only (and sad) option would be to use a setTimeout or (easier) setInterval, continuously checking at defined intervals if the operation has been completed, and then fire the callback.
You could use a callback parameter:
function setFullmap(mapId,callback) {
jen.map = new Map(mapId);
callback();
}
....
samp: function(id, opt){
setFullMap(id,function() {
addNewLayer(opt);
});
}
When u dont have a way to manipulate the Map Object then u need to use a loop:
var loop=self.setInterval(function(){
if(jen.map) {
//execute code here after jen.map is defined
console.log(typeof jen.map);
window.clearInterval(loop);
}
},50);
Check jsfiddle:
http://jsfiddle.net/9yv5t/1/
I have checked the docs and it seems that there are various events you could listen to.
For example:
var m = new Map(...);
m.on('load', function () {
//execute code when the first layer is ready
});
var l = new Layer(...);
l.on('load', function () {
//execute code when the layer has been initialized
});
It's also carefully stated for the Layer.load event:
fires after layer properties for the layer are successfully populated.
This event must be successful before the layer can be added to the
map.
I have a requirement where I get the anchor tags id and based on the id I determine which function to execute.. so is there anything that suites below code
function treeItemClickHandler(id)
{
a=findDisplay(id);
a();
}
You can assign a function to a variable like so:
You can also return a function pointer from a function - see the return statement of findDisplay(id).
function treeItemClickHandler(id)
{
var a= findDisplay;
var other = a(id);
other();
}
function findDisplay(id)
{
return someOtherThing;
}
function someOtherThing()
{
}
Sure, functions are first class objects in JavaScript. For example, you can create a map (an object) which holds references to the functions you want to call:
var funcs = {
'id1': function(){...},
'id2': function(){...},
...
};
function treeItemClickHandler(id) {
if(id in funcs) {
funcs[id]();
}
}
As functions are treated as any other value, you can also return them from another function:
function findDisplay(id) {
// whatever logic here
var func = function() {};
return func;
}
functions are normal javascript values, so you can pass them around, (re)assign them to variables and use them as parameter values or return values for functions. Just use them ;) Your code is correct so far.
You can map between ids and functions to call in a number of ways.
One of the simpler ones is to create an object mapping ids to functions, and find the function to call from that object (this is in essence a nicer-looking switch statement).
Example:
function treeItemClickHandler(id)
{
var idMap = {
"some-id": findDisplay,
"another-id": doSomethingElse
};
if (!idMap.hasOwnProperty(id)) {
alert("Unknown id -- how to handle it?");
return;
}
// Call the corresponding function, passing the id
// This is necessary if multiple ids get handled by the same func
(idMap[id])(id);
}
function findDisplay(id)
{
// ...
}
function doSomethingElse(id)
{
// ...
}
If I have an arbitrary function myFunc, what I'm aiming to do is replace this function with a wrapped call that runs code before and after it executes, e.g.
// note: psuedo-javascript
var beforeExecute = function() { ... }
var afterExecute = function() { ... }
myFunc = wrap(myFunc, beforeExecute, afterExecute);
However, I don't have an implementation of the required wrap function. Is there anything that already exists in jQuery like this (I've had a good look through the docs but cannot see anything)? Alternatively does anybody know of a good implementation of this because I suspect that there are a bunch of edge cases that I'll miss if I try to write it myself?
(BTW - the reason for this is to do some automatic instrumentation of functions because we do a lot of work on closed devices where Javascript profilers etc. are not available. If there's a better way than this then I'd appreciate answers along those lines too.)
Here’s a wrap function which will call the before and after functions with the exact same arguments and, if supplied, the same value for this:
var wrap = function (functionToWrap, before, after, thisObject) {
return function () {
var args = Array.prototype.slice.call(arguments),
result;
if (before) before.apply(thisObject || this, args);
result = functionToWrap.apply(thisObject || this, args);
if (after) after.apply(thisObject || this, args);
return result;
};
};
myFunc = wrap(myFunc, beforeExecute, afterExecute);
The accepted implementation does not provide an option to call wrapped (original) function conditionally.
Here is a better way to wrap and unwrap a method:
/*
Replaces sMethodName method of oContext with a function which calls the wrapper
with it's list of parameters prepended by a reference to wrapped (original) function.
This provides convenience of allowing conditional calls of the
original function within the wrapper,
unlike a common implementation that supplies "before" and "after"
cross cutting concerns as two separate methods.
wrap() stores a reference to original (unwrapped) function for
subsequent unwrap() calls.
Example:
=========================================
var o = {
test: function(sText) { return sText; }
}
wrap('test', o, function(fOriginal, sText) {
return 'before ' + fOriginal(sText) + ' after';
});
o.test('mytext') // returns: "before mytext after"
unwrap('test', o);
o.test('mytext') // returns: "mytext"
=========================================
*/
function wrap(sMethodName, oContext, fWrapper, oWrapperContext) {
var fOriginal = oContext[sMethodName];
oContext[sMethodName] = function () {
var a = Array.prototype.slice.call(arguments);
a.unshift(fOriginal.bind(oContext));
return fWrapper.apply(oWrapperContext || oContext, a);
};
oContext[sMethodName].unwrapped = fOriginal;
};
/*
Reverts method sMethodName of oContext to reference original function,
the way it was before wrap() call
*/
function unwrap(sMethodName, oContext) {
if (typeof oContext[sMethodName] == 'function') {
oContext[sMethodName] = oContext[sMethodName].unwrapped;
}
};
This is the example I would use
<script type="text/javascript">
var before = function(){alert("before")};
var after = function(param){alert(param)};
var wrap = function(func, wrap_before, wrap_after){
wrap_before.call();
func.call();
wrap_after.call();
};
wrap(function(){alert("in the middle");},before,function(){after("after")});
</script>
You could do something like:
var wrap = function(func, pre, post)
{
return function()
{
var callee = arguments.callee;
var args = arguments;
pre();
func.apply(callee, args);
post();
};
};
This would allow you to do:
var someFunc = function(arg1, arg2)
{
console.log(arg1);
console.log(arg2);
};
someFunc = wrap(
someFunc,
function() { console.log("pre"); },
function() { console.log("post"); });
someFunc("Hello", 27);
Which gives me an output in Firebug of:
pre
Hello
27
post
The important part when wrapping this way, is passing your arguments from the new function back to the original function.
Maybe I'm wrong, but I think you can directly create an anonym function and assign it to myFunc:
myFunc = function(){
BeforeFunction();
myFunc();
AfterFunction();
}
In this way you can control the arguments of every function.
This question already has answers here:
Pass an extra argument to a callback function
(5 answers)
Closed 6 years ago.
I want to something similar to this:
function AjaxService()
{
this.Remove = function (id, call_back)
{
myWebService.Remove(id, CallBack)
}
function CallBack(res) {
call_back(res);
}
}
so my calling program will be like this:
var xx = new AjaxService();
xx.Remove(1,success);
function success(res)
{
}
Also if I want to add more parameters to success function how will I achieve it.
Say if I have success function like this:
var xx = new AjaxService();
//how to call back success function with these parameters
//xx.Remove(1,success(22,33));
function success(res,val1, val2)
{
}
Help will be appreciated.
Use a closure and a function factory:
function generateSuccess (var1,var2) {
return function (res) {
// use res, var1 and var2 in here
}
}
xx.Remove(1,generateSuccess(val1,val2));
What you're passing here is not the generateSuccess function but the anonymous function returned by generateSuccess that looks like the callback expected by Remove. val1 and val2 are passed into generateSuccess and captured by a closure in the returned anonymous function.
To be more clear, this is what's happening:
function generateSuccess (var1,var2) {
return function (res) {
// use res, var1 and var2 in here
}
}
var success = generateSuccess(val1,val2);
xx.Remove(1,success);
Or if you prefer to do it inline:
xx.Remove(1,(function(var1,var2) {
return function (res) {
// this is your success function
}
})(val1,val2));
not as readable but saves you from naming the factory function. If you're not doing this in a loop then Xinus's solution would also be fine and simpler than my inline version. But be aware that in a loop you need the double closure mechanism to disconnect the variable passed into the callback function from the variable in the current scope.
You can pass it as anonymous function pointer
xx.Remove(1,function(){
//function call will go here
success(res,val1, val2);
});
one way to do this:
function AjaxService {
var args_to_cb = [];
this.Remove = function (id, call_back, args_to_callback_as_array) {
if( args_to_callback_as_array!=undefined )
args_to_cb = args_to_callback_as_array;
else
args_to_cb = [];
myWebService.Remove(id, CallBack)
}
function CallBack(res) {
setTimeout( function(){ call_back(res, args_to_cb); }, 0 );
}
}
So you can use it like this:
var service = new AjaxService();
service.Remove(1,success, [22,33]);
function success(res,val1, val2)
{
alert("result = "+res);
alert("values are "+val1+" and "+val2);
}
I usually have the callback execute using a setTimeout. This way, your callback will execute when it gets the time to do so. Your code will continue to execute meanwhile, e.g:
var service = new AjaxService();
service.remove(1, function(){ alert('done'); }); // alert#1
alert('called service.remove'); // alert#2
Your callback will execute after alert#2.
Of course, in case of your application, it will happen so automatically since the ajax callback itself is asynchronous. So in your application, you had better not do this.
Cheers!
jrh