10

I know that javascript does not use Class, at least not in common sense`. I will like to know how to return and save an AJAX return value in a class variable rather than calling multiple methods within the callback.

var Reader = function(){
 //Initialize some variables
 this.data = null;
}
Reader.prototype.makeAjaxCall = function(urlPath){
 //Make and Ajax call and return some value
 Ajax.success(function(data){
 this.data = data;
 });
}
Reader.prototype.searchData = function(param){
 //Needs access to this.data
}
Reader.prototype.findData = function(id){
 //Needs access to this.data
}
Reader.prototype.deleteItem = function(id){
 // Needs access to this.data
}

In the above code, whatever function that needs access to the data property needs to be called within the ajax success callback, so If I have ten methods that need data, I will have to line all of them up within the callback, which I do not feel is right. How do I minimise the number of functions in the callback and ensure in some other ways that the function is successful and data is saved the instance variable data.

asked Mar 14, 2016 at 11:47
4
  • there are multiple ways to solve this, it depends on what exactly is the behavior you need Commented Mar 14, 2016 at 11:53
  • You can set data = undefined for error in AJAX call or create some error flag. So whenever data is undefined or you have error flag - you know if your AJAX call was success. Commented Mar 14, 2016 at 11:56
  • you can use $.extend function to marge all the function and add different property Commented Mar 14, 2016 at 12:03
  • 2
    I doubt that this in Ajax.success(function(data){this.data = data;}); works. You best use arrow functions, self = this; or bind(this); Commented Mar 22, 2016 at 15:30

13 Answers 13

7
+25

The essensential part is how to handle asyncronous data with JavaScript. There are some well tested solutions to this: functions and promises.

In both cass the Reader should have a constructor which assigns the data like this:

function Reader(data) {
 this.data = data;
}

The callback approach requires a factory function, which has a callback.

function getReader(url, callBack) {
 Ajax.success(function(data){
 callBack( new Reader(data) );
 });
}

And use it like

getReader(url, function(reader) {
 reader.searchData();
});

The Promise -approach does not require immediately a callback so the result can be stored in a variable and passed around as a variable which has some advantages:

function getReaderPromise(url) {
 return new Promise( function( resolve, reject ) {
 Ajax.success(function(data){
 resolve( new Reader(data) );
 }); 
 });
}

However, using the promise usually requires calling the then function of the promise:

getReaderPromise(url).then( function(reader) {
 reader.searchData();
});
// Fat arrow syntax
getReaderPromise(url).then( (reader) => {
 reader.searchData();
}); 

In the future you can get rid of the callbacks with Promises using ES6 generators with yield like

let reader = yield getReaderPromise( url );

As explained in here: https://davidwalsh.name/async-generators

answered Mar 17, 2016 at 15:54

Comments

4

You could try something like:

var Reader = function(){
 //Initialize some variables
 this.data = null;
}
Reader.prototype.makeAjaxCall = function(urlPath){
 //Make and Ajax call and return some value
 this.data = Ajax({url: urlPath}); // return promise
}
Reader.prototype.searchData = function(param){
 //Needs access to this.data
 if(this.data){
 this.data.then(...);
 }
}
Reader.prototype.findData = function(id){
 //Needs access to this.data
 if(this.data){
 this.data.then(...);
 }
}
Reader.prototype.deleteItem = function(id){
 // Needs access to this.data
 if(this.data){
 this.data.then(...);
 }
}
answered Mar 17, 2016 at 9:32

Comments

3

In your code:

Reader.prototype.makeAjaxCall = function(urlPath){
 //Make and Ajax call and return some value
 Ajax.success(function(data){
 this.data = data;
 });
}

"this" is not the Reader instance context, you should change it to

Reader.prototype.makeAjaxCall = function(urlPath){
 var myThis = this;
 //Make and Ajax call and return some value
 Ajax.success(function(data){
 myThis .data = data;
 });
}

To keep the pointer to Reader instance in myThis variable and access inside the success function

answered Mar 23, 2016 at 12:33

Comments

1

this is what I think is a nice way to solve it, it needs a couple of callbacks though

var Reader = function(){
 //Initialize some variables
 this.data = null;
}
Reader.prototype.makeAjaxCall = function(urlPath, cb) {
 //Make and Ajax call and return some value
 Ajax.success(function(data) {
 this.data = data;
 if(cb) cb();
 });
}
Reader.prototype.searchData = function(param){
 var self = this;
 if(!this.data) {
 // data doesn't exist, make request
 this.makeAjaxCall(urlPath, function() {
 // when is ready, retry
 self.searchData(param);
 });
 return;
 }
 // do whatever you need...
 // data exists
}
Reader.prototype.findData = function(id){
 var self = this;
 if(!this.data) {
 // data doesn't exist, make request
 this.makeAjaxCall(urlPath, function() {
 // when is ready, retry
 self.findData(id);
 });
 return;
 }
 // do whatever you need...
 // data exists
}
Reader.prototype.deleteItem = function(id){
 var self = this;
 if(!this.data) {
 // data doesn't exist, make request
 this.makeAjaxCall(urlPath, function() {
 // when is ready, retry
 self.deleteItem(id);
 });
 return;
 }
 // do whatever you need...
 // data exists
}
answered Mar 14, 2016 at 12:06

Comments

1

Your problem is about the confusion on 'this' key.

When you use this in a function, it uses the context of that function.

So this is slightly using a different context:

Reader.prototype.makeAjaxCall = function(urlPath){
 //Make and Ajax call and return some value
 Ajax.success(function(data){
 this.data = data;
 });
}

than this:

Reader.prototype.makeAjaxCall = function(urlPath){
 var readerContext = this;
 //Make and Ajax call and return some value
 Ajax.success(function(data){
 readerContext.data = data;
 });
}

and this is the solution for your problem.

answered Mar 22, 2016 at 9:53

Comments

1

In order to save the return data in a so called Class variable, given your example you need to understand the context where you are using the this keyword. In your example

Reader.prototype.makeAjaxCall = function(urlPath){
 //Make and Ajax call and return some value
 Ajax.success(function(data){
 this.data = data;
 });
}

this is referred to the Ajax object.

Since this.data in Reader object is pretty much encapsulated in the context of the Reader object you need to save it locally before making the Ajax call, in order to be able to access it from withing the call. Like this:

Reader.prototype.makeAjaxCall = function(urlPath){
 var _this = this;
 //Make and Ajax call and return some value
 Ajax.success(function(data){
 _this.data = data;
 });
}

This pretty much solves the context problem and is very helpful when it comes to making function calls. A simple solution is to just define a local variable where you can store the data temporary and then use it to overwrite the the value of this.data Code should look like this:

 Reader.prototype.makeAjaxCall = function(urlPath){
 var tempData;
 //Make and Ajax call and return some value
 Ajax.success(function(data){
 tempData = data;
 });
 this.data = tempData;
 }
answered Mar 23, 2016 at 10:07

Comments

0

You could do something like

 Reader.prototype.searchData = function(param){
 var self = this;
 if (!this.data) {
 setTimeout(function() { self.searchData(param) }, 1000);
 return;
 }
 ...
 }
answered Mar 14, 2016 at 11:57

Comments

0

Try to change this:

Reader.prototype.makeAjaxCall = function(urlPath){
 //Make and Ajax call and return some value
 Ajax.success(function(data){
 this.data = data;
 });
}

to this:

Reader.prototype.makeAjaxCall = function(urlPath){
 var _this = this;
 //Make and Ajax call and return some value
 Ajax.success(function(data){
 _this.data = data;
 });
}
answered Mar 17, 2016 at 20:25

Comments

0

One simple solution would be to put all the function calls within another function and only call that one from the callback:

var Reader = function(){
 //Initialize some variables
 this.data = null;
}
Reader.prototype.makeAjaxCall = function(urlPath){
 //Make and Ajax call and return some value
 var that = this;
 Ajax.success(function(data){
 that.data = data;
 that.makeOtherCalls();
 });
};
Reader.prototype.makeOtherCalls = function(){
 // call the functions that need this.data here
};
answered Mar 22, 2016 at 18:19

Comments

0

people are obsessed with using prototyping to create functions within a class, but there is a cleaner way, by using functions with the 'this' reference. Then you can have access to the inner workings of your class through a self variable.

eg:

var Reader = function(){
 var self = this;
 //Initialize some variables
 this.data = null;
 this.makeAjaxCall = function(urlPath){ 
 Ajax.success(function(data){
 self.data = data;
 });
 }
}

Then in your prototype methods you can use the data with:

this.data

You can call the ajax method using:

this.makeAjaxCall(url);

And you can call the ajax method from outside of the class using:

var myReader = new Reader();
myReader.makeAjaxCall(url);

as per normal.

answered Mar 23, 2016 at 2:52

Comments

0

MAKE USE OF GLOBAL JS VARIABLE

You can simply make use of global JS variables for this kind of requirement. Please check the code below

var globalData; /* This is global.It can be accessed anywhere */
var Reader = function(){
 //Initialize some variables
 globalData = null;
}
Reader.prototype.makeAjaxCall = function(urlPath){
 //Make and Ajax call and return some value
 Ajax.success(function(data){
 globalData = data;
 });
}
Reader.prototype.searchData = function(param){
 //Access globalData here
}
Reader.prototype.findData = function(id){
 //Access globalData here
}
Reader.prototype.deleteItem = function(id){
 //Access globalData here
}
answered Mar 23, 2016 at 7:20

4 Comments

Global variables are bad, especially if they are accessed by multiple instances of the same class.
Really? It is just increasing the scope. This is similar to any other object variable or a function variable
Don't take my word for it, but read this: stackoverflow.com/questions/10525582/…
@dburner Nope. I disagree. It depends on how and how often you use global variables. This requirement takes only one global variable and it is fine.
0

I have developed my own jQuery ajax wrapper for this, but it does require jQuery... If that helps check my github repo

JQPS Framework v. 1.1b - Github

This can help you a lot, it uses OOP by namespaces(the classes that you are looking for) like Myobject.vars.anything = value (from callback)

Example of call

Main.ajaxCall('auth','login','POST','TEXT',{dataObj},{Utils:{1:'setVar|Main,myVar,,callback',2:'anyOtherFunction'}},false);

This will trigger an ajax call to http://yoursite.com/auth/login with the dataObj as post object, at the callback the first function will be called, in our case the Utils.setVar() this will store the response from the callback in the Main.vars.myVar as we defined it in the callback object, after that the second function will be triggered and executed as defined in the call Utils.anyOtherFunction()... you can add how many functions you want in the callback.

For more information you can check the readme from github, this is set for sync and async calls but it does require jQuery, it does not work without it.

answered Mar 23, 2016 at 13:21

Comments

0

You can use context setting of $.ajax() to set this at $.ajax() callback, deferred.then(); include error handling function within Reader instance.

// set `this` at `$.ajax()` to context of `Reader` instance
var Reader = function(context) {
 //Initialize some variables
 this.data = null;
 // handle `$.ajax()` errors
 this.err = function(jqxhr, textStatus, errorThrown) {
 console.log(textStatus, errorThrown, this);
 }
 this.makeAjaxCall = function(urlPath) {
 //Make and Ajax call and return some value
 return $.ajax({
 url: urlPath,
 // set `context` to `this`
 context: context || this,
 })
 .then(function(data) {
 this.data = data;
 console.log("in `Reader.makeAjaxCall`, this is:", this);
 }, this.err);
 }
 this.searchData = function(param) {
 //Needs access to this.data
 console.log("in `Reader.searchData`", this.data);
 }
 this.findData = function(id) {
 //Needs access to this.data
 console.log("in `Reader.findData`", this.data);
 }
 this.deleteItem = function(id) {
 // Needs access to this.data
 console.log("in `Reader.deleteItem`", this.data);
 }
}
var reader = new Reader();
reader.makeAjaxCall("")
.then(reader.searchData)
.then(reader.findData)
.then(reader.deleteItem)
.then(function() {
 console.log("complete", this.data, this)
})
answered Mar 22, 2016 at 18:32

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.