Callback losing scope of containing function - javascript

I have a code like that:
{
// other functions...
myFunction: function( item, options, callback ) {
var self = this;
// Both item and options variables are accessible here.
try {
var content = this.getContent( item );
} catch(ex) {
// Exception handling here....
}
editor.saveContent( content, function() {
// OPTIONS IS UNDEFINED HERE!
// ITEM IS UNDEFINED HERE!
// Callback, instead, is accessible!
if ( callback ) { callback() };
});
}
}
Problem is that inside the saveContent callback, I can access the callback variable, while any attempt in accessing the item, options and content vars are unsuccessful! Why is that?

You should supply the variable you are interrested in to the callback function like this:
editor.saveContent( content, function() {
if ( callback ) { callback(item, options) };
});
Via javascript closure, the item and options variable are available in the saveContent function.
Created fiddle to demonstrate (see console log).

The code you posted should work fine. But if that is really not working then try writing your code like this:
{
// other functions...
myFunction: function( item, options, callback ) {
var self = this;
// Store the reference in "this"
self.item = item;
self.options = options;
self.callback = callback;
// Both item and options variables are accessible here.
try {
self.content = this.getContent( item );
} catch(ex) {
// Exception handling here....
}
editor.saveContent( self.content, function() {
// Use them here
console.log(this.item, this.options, this.content);
// Callback, instead, is accessible!
if ( this.callback ) { this.callback() };
}.bind(self));
}
}
You have written var content inside the try-catch block. So it anyways can't be accessible outside the try-catch block.

editor.saveContent.call(this,content, function(){
//can access any variable defined in its parent scope
});

Related

How to make my JS callback work right?

I have the following problem:
I'm trying to implement a Callback in JavaScript. Now I just made it with a global variable which holds my callbacl function. Here is the example:
_callbackFkt = null;
requestCompleted = function(oControlEvent) {
console.log("Callback: " + _callbackFkt.toString());
};
myLibRequest = function(callback) {
// some code, which is attached to the requestComplete event when ready
_callbackFkt = callback;
};
Now I try to call the functions which use the callback:
myLibRequest(function () {
// callback function 1
});
myLibRequest(function () {
// callback function 2
});
myLibRequest(function () {
// callback function 3
});
the result in the console is:
Callback: function () {
// callback function 3
}
How can I define the callback to be bound to one function call and not global available? I want the result:
Callback: function () {
// callback function 1
}
Callback: function () {
// callback function 2
}
Callback: function () {
// callback function 3
}
There are several ways to do what you are trying to do, but your basic problem is that you want a list of event handlers, but you are only assigning a single value.
To modify what you are currently doing:
_callbackFkts = [];
myLibRequest = function(callback) {
// some code, which is attached to the requestComplete event when ready
_callbackFkts.push(callback);
};
Then, when you want to execute the callbacks:
_callbackFkts.forEach(function(callbackFkt) {
callbackFkt();
});
But, this global mechanism is a bit messy. You might consider some encapsulation (untested, but you get the idea):
function Events() {
this.callbacks = [];
}
Events.protototype.bind = function(callback) {
this.callbacks.push(callback);
};
Events.prototype.executeAll = function(params) {
this.callbacks.forEach(function(callback) {
callback.apply(this, params);
}
}
Then you can use it like this:
var events = new Events();
events.bind(function() {
//callback function 1
});
events.bind(function() {
//callback function 2
});
events.bind(function() {
//callback function 3
});
events.executeAll('with', 'parameters');
Finally, you might just use an off-the-shelf event library. There are lots. One quick google search finds this.
Having a global as the callback will only work if myLibRequest() contains only synchronous code (which I assume it doesn't).
Remove the global, and use the callback that is passed in as an argument.
Assuming you have some async call in there, and you call requestCompleted when it's done. Add an argument so requestCompleted receives the callback, instead of referenceing the global.
requestCompleted = function(oControlEvent, callback) {
console.log("Callback: " + callback.toString());
};
myLibRequest = function(callback) {
myAsyncFunction(function(){
// async complete
requestCompleted('event', callback);
});
};

Check for function called

Just wondering if there is anyway to fire some code when a function is called, without adding the code to the function, for example:
function doSomething(){
//Do something
}
//Code to call when doSomething is called
You can wrap the function :
(function(){
var oldFunction = doSomething;
doSomething = function(){
// do something else
oldFunction.apply(this, arguments);
}
})();
I use an IIFE here just to avoid polluting the global namespace, it's accessory.
Well, yes, it's not actually hard to do. The crucial thing is that a function's name is just an identifier like any other. You can redefine it if you want to.
var oldFn = doSomething;
doSomething = function() {
// code to run before the old function
return oldFn.apply(this, arguments);
// code to run after the old function
};
NB that it's better to do oldFn.apply(this, arguments) rather than just oldFn. In many cases it won't matter, but it's possible that the context (i.e. the value of this inside the function) and the arguments are important. Using apply means they are passed on as if oldFn had been called directly.
What about something like:
function doSomething(){
doSomething.called = true;
}
//call?
doSomething();
if(doSomething.called) {
//Code to call when doSomething is called
}
I know you said you don't want to modify the original function, but consider adding a callback. Then you can execute code based on different results in your function (such as onSucess and onError):
function doSomething(onSuccess, onError){
try {
throw "this is an error";
if(onSuccess) {
onSuccess();
}
} catch(err) {
if(onError) {
onError(err);
}
}
}
Then, when you call doSomething, you can specify what you want done with inline functions:
doSomething(function() {
console.log("doSomething() success");
}, function(err) {
console.log("doSomething() error: " + err);
});

Pass this to callback

I know what the problem is but not sure what's the best option to solve this issue. I have got a callback and I'm not able to access this from it. I don't want to have any variable outside the scope to refer this. Can I pass this as a parameter?
var myModule = Module.create({
init: function() {
ws.subscribe('/topic/notifications', this._wsNotifications, headers);
},
refresh: function() {
},
_wsNotifications: function ( message ) {
this.refresh(); //Error: 'this' is undefined because it's a callback
}
});
One way you can solve this is using function.bind at the source When you specify the callback do
ws.subscribe('/topic/notifications', this._wsNotifications.bind(this), headers);
or cache the this to a variable.
var myModule = Module.create({
self : this,
init: function() {
ws.subscribe('/topic/notifications', this._wsNotifications, headers);
},
refresh: function() {
},
_wsNotifications: function ( message ) {
self.refresh(); //Error: 'this' is undefined because it's a callback
}
});
Give this a try.
var myModule = Module.create({
var self = this;
init: function() {
ws.subscribe('/topic/notifications', this._wsNotifications, headers);
},
refresh: function() {
},
_wsNotifications: function ( message ) {
self.refresh(); //Error: 'this' is undefined because it's a callback
}
});
return interactions;
});
note the creation and use of the self variable instead of the this variable. Using this method will preserve this, even when it would normally change scope.
You can make use of ECMAscript's bind function Function.prototype.bind.
init: function() {
ws.subscribe('/topic/notifications', this._wsNotifications.bind(this), headers);
},
Now, this within _wsNotifications will refer to the object you bound it to.

js execute function after object is defined

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.

js callback function

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.

Categories