using getJSON in a class (scoping issue) - javascript

//this is a class
function SpriteSheetClass()
{
this.sprite="local";
$.getJSON(url, this.onLoaded);
}
SpriteSheetClass.prototype.onLoaded= function(json)
{
console.log(this.sprite); //returns undefined!
//I am out of SpriteSheetClass scope! how do I store the json???
}
var a=new SpriteSheetClass()
When the getJSON callback is executed, the scope is not in the class.
There is no way to bind the returned json with my class instance (a)
Keep in mind I want to keep the calls async, and I need to create many instances of SpriteSheetClass
This is not a timing question, it is a scope question.
thanks

When the callback function is called, this will not be the object, but the $.getJSON function.
You can bind the value of this to the callback, so it will be the object instead
$.getJSON(url, this.onLoaded.bind(this));
FIDDLE

With modern day browsers you can use function.bind(thisArg) to keep the proper scope.
function SpriteSheetClass() {
this.sprite="local";
$.getJSON(url, this.onLoaded.bind(this));
}

A local wrapper function will do the job:
function SpriteSheetClass()
{
this.sprite="local";
var that = this;
var callback = function(data) {
that.onLoaded(data);
};
$.getJSON(url, callback);
}

Related

How to make JS function wait for JSON data to load?

This works:
function getDataJSON() {
var queryString = "https://www.examplesite.com/someJSON.json";
$.getJSON(queryString, function(data) {
doStuffWithData(data)
});
}
function doStuffWithData(JSON) {
// some code that refers to JSON
}
getDataJSON();
But this complains that a variable (JSON) is undefined somewhere in doStuffWithData():
function getDataJSON(callback) {
// Gets share data and runs callback when received
var queryString = "https://www.examplesite.com/someJSON.json";
$.getJSON(queryString, function(data) {
if(typeof callback === "function") {
callback(data);
}
});
}
function doStuffWithData(JSON) {
// some code that refers to JSON
}
getDataJSON(doStuffWithData());
What am I likely to be doing wrong? The $.getJSON() call takes a second or two so I do need to wait for that and do stuff after it. I think the issue is with the code execution order, but it could be that I've misunderstood how to properly pass the data to the callback function.
It would be better if I could just load the data into a variable all my other functions can access.
This:
getDataJSON(doStuffWithData());
should be this:
getDataJSON(doStuffWithData);
Otherwise it invoked that function immediately and attempts to pass the result of the function into getDataJSON
You need to leave out the parenthesis in this call
getDataJSON(doStuffWithData()); should be getDataJSON(doStuffWithData). The reason is that doStuffWithData is the function and doStuffWithData() is the retrun value from the function.

Listen for a function to be called JavaScript

I have the following functions that is called every 2 seconds to load some data. It registers the function [do] to do the stuff with the response. (the example is simplified).
function doRequest (){
$.ajax({ url: 'www.google.com.pe', success: function (response) {do(response)} });
}
function do (text){
var i = setInterval(doRequest, 2000);
}
I wonder if there is any way that I can create a function that is called every time the [do] function is called with out needing to add a call to the listener inside the do function. If there is any better way to do it with jQuery, like a plugin I'd appreciate the help.
[Edit] The idea is not whether it works or not. My question was about if I can add a custom listener to the "do" function which was already implemented. Something like addActionListener("do", "after", doSomeThingElse),sSo I could do some thing else just after the do function has finished.
First, your simplified version won't work, because you'd need to pass the do function instead of calling it.
function doRequest (){
$.ajax({ url: 'www.google.com.pe', success: _do });
}
But it sounds like you're asking how to run some other code every time do is invoked.
If do is only invoked inside the doRequest() function, then just add your other code to an anonymous function that invokes do at the right time.
function doRequest (){
$.ajax({ url: 'www.google.com.pe', success: function(response) {
// Run your other code
// or invoke another function.
_do(response);
} });
}
If you want it to be more generalized, you can create a function decorator that returns a function which invokes do after some other code.
function doFactory(fn) {
return function() {
fn.apply(this, arguments);
_do.apply(this, arguments);
}
}
then make functions like this:
var doFoo = doFactory(function() {
console.log("foo");
});
If your requirement is more specific of a pre-processing of response, you could rework it like this:
function doFactory(fn) {
return function(response) {
_do.call(this, fn.call(this, response));
}
}
Then have the fn manipulate and return response.
var doFoo = doFactory(function(response) {
return response + "foo";
});
If you want to keep existing code as it is, you could wrap do() in another function which in turn calls do() and your new function (say do_this_as_well()).
See the example below (I renamed do() to do_this() to avoid confusion around the reserved keyword do). This works because global functions are nothing but variables with function objects in them. These variables can be overwritten, in this case with a new function that calls the old one:
function do_this(response) { ... }
(function()
{
var previous=do_this;
do_this=function(response) { previous(response); do_this_as_well(); }
})();
Replace
success: do(response)
with
success: function(response) { do(response); do_this_as_well(); }

javascript prototype access this

I have a javascript 'class' which contains a wrapper method to call jquery .ajax(). I want to pass in the onSuccess and onError function handlers, but am not sure how. I can do this with plain old global functions, but I'm trying to improve my javascript (from Java background). Any pointers would be appreciated.
In the _makeAjaxCall() method below, how do I reference the onSuccessHandler
function testApp() {
new X();
}
function X() {
// Init X by making Ajax call, passing the func to be called on ajax return
this._makeAjaxCall(initUrl, this.onSuccessInit, this.onError);
// Make another ajax call to init another component
this._makeAjaxCall(initUrl, this.onSuccessSomeOtherAjaxCall, this.onError);
}
X.prototype.onSuccessInit = function(){
this.doStuff(...);
}
X.prototype.onSuccessSomeOtherAjaxCall = function(){
this.doOtherStuff(...);
}
/**
* make an ajax call, and call the provided success/error handler
*/
X.prototype._makeAjaxCall = function(url, onSuccessHandler, onError){
$.ajax({
url : url,
success : function (jsonData, textStatus, XMLHttpRequest) {
// If I don't user 'this', the func called but I've lost my reference
// to my instance of X
onSuccessHandler();
// If I use 'this', it points to the ajax call object, not to my X object.
this.onSuccessHandler();
}
});
}
The problem is that when the success callback is called by the $.ajax function, the default context is used window. You need to tell JQuery that you want a different context, so you can do one of 3 things:
Add a context attribute to the hash that is sent to $.ajax, so I your case you can do:
$.ajax({
url: url,
context: this, // this will tell JQuery to use the right context
success: this.onSuccessHandler
});
Use JQuery's $.proxy function, like:
$.ajax({
url: url,
success: $.proxy(this.onSuccessHandler, this) // this will bind the correct context to the callback function
});
Cache the variable this, like #mVChr suggested, although I would encourage you to use self as it has become somewhat of a javascript idiom
var self = this;
$.ajax({
url: url,
success: function(data) {
self.onSuccessHandler(data);
}
});
Edit:
If you need a more in depth explanation of context and scope in javascript checkout this article: http://www.digital-web.com/articles/scope_in_javascript/
Cache this within the local scope of _makeAjaxCall before conducting the ajax call:
X.prototype._makeAjaxCall = function(url, onSuccessHandler, onError){
var _X = this; // cache this
$.ajax({
url : url,
success : function (jsonData, textStatus, XMLHttpRequest) {
// use cached this
_X.onSuccessHandler();
}
});
}
Thanks to input from CarlosZ & mVChr, I've figured out the solution, http://jsfiddle.net/bX35E/3/
$(document).ready(function testApp() {
new X();
});
function X() {
console.dir(this);
var initUrl = "/echo/json/";
this._instanceVariable = "I AM defined!";
// Init X by making Ajax call, passing the func to be called on ajax return
this._makeAjaxCall(initUrl, this.onSuccessInit(), this.onError);
// Make another ajax call to init another component
this._makeAjaxCall(initUrl, this.onSuccessSomeOtherAjaxCall(), this.onError);
}
X.prototype.onSuccessInit = function(){
//this.doStuff(...);
var self = this;
return function() {
alert("onSuccessInit, _instanceVariable="+self._instanceVariable);
}
}
X.prototype.onSuccessSomeOtherAjaxCall = function(){
var self = this;
return function() {
alert("onSuccessSomeOtherAjaxCall, _instanceVariable="+self._instanceVariable);
}
}

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.

Javascript callback functions execution

I'm not sure the correct term for this. But I want to write a function that accepts another function and execute it.
For eg.
function test(data, aFunc) {
var newData = data + " Shawn";
aFunc.call(newData);
}
test("hello", function(data){
alert(data);
});
Data is supposed to contain "hello Shawn" string. Help me rewrite this the correct way please.
The first argument of the call method is used to set the this keyword (the function context) explicitly, inside the invoked function, e.g.:
function test(data, aFunc) {
var newData = data + " Shawn";
aFunc.call(newData);
}
test("hello", function () {
alert(this); // hello Shawn
});
If you want to invoke a function without caring about the context (the this keyword), you can invoke it directly without call:
function test(data, aFunc) {
var newData = data + " Shawn";
aFunc(newData); // or aFunc.call(null, newData);
}
test("hello", function (data) {
alert(data);
});
Note that if you simply invoke a function like aFunc(newData); or you use the call or apply methods with the this argument set as null or undefined, the this keyword inside the invoked function will refer to the Global object (window).
Looks fine but you can just change
aFunc.call(newData);
to
aFunc(newData);
You were close. The first argument to "call" is the "scope" argument. In this case, it doesn't matter what it is, because you're not using "this" anywhere in your anonymous function, so any value will suffice.
function test(data, aFunc) {
var newData = data + " Shawn";
aFunc.call(this, newData);
}
test("hello", function(data){
alert(data);
});

Categories