I am recently working on small chat module , which require continuously checking the server for new message.
I am sending a ajax request to a server , and the server hold's the connection until new message is found(long polling).
Code :
var chatController = function(){
//other variable declaration
/**
* Ajax call to monitor the new message , on complete of ajax call sending other call
*/
this.checkNewMessage = function(){
console.log(this); // placed this for debugging purpose
$.ajax({
url : SITEURL.CHECK_MESSAGE,
data : this.currrentUserDetails,
dataType : 'json' ,
cache : false,
success :(function(obj){
//temp = obj;
return obj.parseNewMessageResponse;
})(this),
complete: (function(obj){
//temp = obj;
return obj.checkNewMessage;
})(this),
});
};
// other function and variable
});
When i tried to call
var mainController = new chatController();
mainController.checkNewMessage();
Problem
What i thought was that i would be able to send continuous single request to server, but to my surprise I only could send 2 ajax request one after the other.
My Debugging
When i tried to debug , i traced out that for the first call this object being passed points to the chatController
complete: (function(obj){
return obj.checkNewMessage;
})(this), // this here point to chatController object
For the second time this object being passed points to the ajax object
complete: (function(obj){
return obj.checkNewMessage;
})(this), // this here point to ajax object
I am using JavaScript closure to pass the chatController object to complete parameter of jquery
So what i want is way to pass parameter to jQuery complete function so that it's point to my original reference
There are various posible cross-browser solutions
You can use $.proxy:
In my opinion, the best practice.
$.ajax({
//...
success: $.proxy(function(json) {
// `this` refers to the second argument of `$.proxy`
}, this)
});
You can set the context option:
$.ajax({
//...
context: this,
success: function(json) {
// `this` refers to the value of `context`
}
});
Or use a closure:
var self = this;
$.ajax({
//...
success: function(json) {
// `this` refers to ajax request, use self instead
$(self).dosomething();
}
});
There are at least four different ways to solve the issue of not calling your method with the right context from your success and complete handlers.
Use the context argument for $.ajax() so that this in your success handler will be what you want it to be so you can then call your method.
Use .bind() to create a new function stub that calls your method with the right context.
Save the value of this into a local variable so you can reference that variable when you need it in a stub function.
Use jQuery's cross browser version of .bind() which is called $.proxy().
I will offer you some examples of each.
First, the context option for $.ajax():
this.checkNewMessage = function(){
console.log(this); // placed this for debugging purpose
$.ajax({
context: this,
url : SITEURL.CHECK_MESSAGE,
data : this.currrentUserDetails,
dataType : 'json' ,
cache : false,
success : function(data) {
this.parseNewMessageResponse(data);
},
complete : function(data) {
this.checkNewMessage();
}
});
};
Then, using .bind().
this.checkNewMessage = function(){
console.log(this); // placed this for debugging purpose
$.ajax({
url : SITEURL.CHECK_MESSAGE,
data : this.currrentUserDetails,
dataType : 'json' ,
cache : false,
success : this.parseNewMessageResponse.bind(this),
complete : this.checkNewMessage.bind(this)
});
};
Then, using a saved copy of this:
this.checkNewMessage = function(){
var self = this;
console.log(this); // placed this for debugging purpose
$.ajax({
url : SITEURL.CHECK_MESSAGE,
data : this.currrentUserDetails,
dataType : 'json' ,
cache : false,
success : function(data) {
self.parseNewMessageResponse(data);
},
complete : function(data) {
self.checkNewMessage();
}
});
};
And finally with jQuery's `.proxy():
this.checkNewMessage = function(){
console.log(this); // placed this for debugging purpose
$.ajax({
url : SITEURL.CHECK_MESSAGE,
data : this.currrentUserDetails,
dataType : 'json' ,
cache : false,
success : $.proxy(this.parseNewMessageResponse, this),
complete : $.proxy(this.checkNewMessage, this)
});
};
If you don't need IE8 support or you're fine with installing a polyfill for .bind(), then the .bind() option is my favorite because it just seems the cleanest.
The easiest way to solve this is to define a reference to your original this so you can access it from another context. Check this simple example:
(function(){
var _self = this;
function changeColor($element, color){
$element.css("background-color", color)
}
$(".recolor-btn").click(function(){
var self = this;
$.ajax({
url: "/Color/GetRandom",
success: function(color){
_self.changeColor($(self), color);
}
});
});
})();
Related
I'm looking to get what I thought would be a simple script to run an AJAX call and keep various values stored to an object, but I cannot get the globals to remain consistent the way I would expect.
I've gone around in circles trying what I think is everything. As soon as I put the AJAX call in I can't get it to play nicely with the global variables. The process value is always false that way and the content never loads in.
ExtContent = function(){
var self = this;
this.init = function() {
self.output = null;
self.process = false;
};
this.request = function(url){
$.ajax({
type : 'GET',
timeout : 10000,
dataType : 'html',
url : url,
passself : self,
success : function(response){
this.passself.setoutput(response);
},
error : function(req,response){
if(response==='error'){
self.error=req.statusText;
}
}
});
};
this.setoutput = function(data){
this.output = data;
this.process = true;
};
this.returnprocess = function(){
return self.process;
};
this.returnoutput = function(){
return self.output;
};
self.init();
};
<div id="holder"></div>
loadcontent = new ExtContent();
loadcontent.request('/test.html');
if(loadcontent.returnprocess()){
$('#holder').before(loadcontent.returnoutput());
}else{
$('#holder').before('FAILED');
}
I can't get the process to be true and the content to be stored in output.
Thanks.
Despite wrapping everything as a class/object, the jQuery $.ajax call is still an asynchronous operation. basically "You have ordered a pizza, then try to eat it before it arrives".
i.e. this orders it:
loadcontent.request('/test.html');
and this tries to eat it immediately:
if(loadcontent.returnprocess()){
The call to setoutput (i.e. the "Pizza delivery") happens long after these operations complete.
You need to add event handler properties to your class, or use deferreds+promises to wait for the data to arrive.
To use promises, just return the $.ajax result from request:
this.request = function(url){
return $.ajax({
type : 'GET',
timeout : 10000,
dataType : 'html',
url : url,
passself : self,
success : function(response){
this.passself.setoutput(response);
},
error : function(req,response){
if(response==='error'){
self.error=req.statusText;
}
}
});
};
and use it like this:
loadcontent.request('/test.html').done(function(){
if(loadcontent.returnprocess()){
$('#holder').before(loadcontent.returnoutput());
}else{
$('#holder').before('FAILED');
}
});
Or if you setup the return values correctly inside request:
loadcontent.request('/test.html').done(function(){
$('#holder').before(loadcontent.returnoutput();
}).fail(function(){
$('#holder').before('FAILED');
});
Maybe this can help you
this.setoutput = function(data){
// 'this' here, is refering 'setoutput' function, not ExtContent,
// so ExtContent.process != ExtContent.setoutput.process
// this.output = data;
// this.process = true;
self.output = data;
self.process = true;
};
I am trying to create a database handler class in javascript. I would like to call the class by simply using:
var databaseHandler = new DatabaseHandler();
result = databaseHandler.getResult("SELECT * FROM login");
I have created the class and used a callback for the ajax function (so as to wait for the ajax result to be returned). But all I am still receiving "undefined" as my result. If I use console.log(a) inside of the onComplete function, I get an array of the intended results.
(function(window){
//Database class
function DatabaseHandler(){
//Query
this.query = function(query, whenDone){
request = $.ajax({
url: "../optiMizeDashboards/php/DatabaseQuery.php",
type: "POST",
data: {query : query},
dataType: "JSON"
});
request.done(function(output) {
whenDone(output);
});
request.fail(function(jqXHR, textStatus) {
console.log(textStatus);
});
};
//Get result
this.getResult = function(query){
this.query(query, this.onComplete);
};
//Ajax callback
this.onComplete = function(a){
return a;
};
}
//Make available to global scope
window.DatabaseHandler = DatabaseHandler;
}(window))
My question is: Is this something to do with the variable scope, or the way that ajax works? I have read all the answers explaining that ajax is ASYNC and I thought I had handled that by using a callback function "onComplete"
Any help on this topic would be greatly appreciated!
You will not be able to return result immediately from calling getResult because underlying jQuery POST request is Asynchronous, instead you need to be passing a callback function which eventually will receive a result from server.
something like that:
(function(window){
//Database class
function DatabaseHandler(){
//Query
this.query = function(query, whenDone){
request = $.ajax({
url: "../optiMizeDashboards/php/DatabaseQuery.php",
type: "POST",
data: {query : query},
dataType: "JSON"
});
request.done(function(output) {
whenDone(output);
});
request.fail(function(jqXHR, textStatus) {
console.log(textStatus);
});
};
//Get result
this.getResult = function(query, callback){
this.query(query, callback);
};
}
//Make available to global scope
window.DatabaseHandler = DatabaseHandler;
}(window))
// then use it like so
var databaseHandler = new DatabaseHandler();
result = databaseHandler.getResult("SELECT * FROM login", function(data) {
//do something with data
});
PS: exposing direct SQL access to the databse on the client is very dangerous though, and I would not recommend doing that
I'm having an issue with calling functions within a loop across different modules using requirejs. The function call within the loop resides in module A and executes a function in module B that fires off an Ajax request using jQuery. Each iteration of the loop fires off a different request with different arguments being passed to module B's function that fires off the Ajax request. When the success function of the Ajax request executes, I find that all my argument values are always the values of the last Ajax call made, for all 4 separate Ajax calls.
I've done some googling and it sounds like this is a pretty common problem when executing a function within a loop. The fix tends to be to break out the function call into a different function, creating a different scope. Since my loop and Ajax calls are in 2 different modules I had assumed this would solve that issue, however it still persists.
I've tried some solutions in other stack overflow posts like:
JSlint error 'Don't make functions within a loop.' leads to question about Javascript itself and How to pass parameter to an anonymous function defined in the setTimeout call? without success. Anyone have any idea?
Sample code for loop module A:
define(["mpos"],
function(mpos){
var monitor = {
startMonitoring : function(poolObj){
// Start Monitoring
$.each(mpos.msgs, function(action,callback){
poolObj.action = action;
mpos.sendApiRequest(poolObj,action,callback);
});
}
};
return monitor;
}
);
Sample code for Ajax module B - this module is referenced as mpos in module A
define(["mule","constants"],
function(mule,constants){
var mpos = {
sendMessage : function(postData,callback,$poolOut){
return $.ajax({
'type':'post',
'url':constants.URLS.proxy,
'data':{'url':postData},
success : function(data){
// if we have $poolOut we know this is a mpos call
if($poolOut != undefined){
var keys = Object.keys(data);
// add poolOut to data
data.poolOut = $poolOut;
var poolObj = $poolOut.data('poolObj');
if(poolObj){
var action = poolObj.action;
console.log(poolObj,action);
if(action){
if(action == "getuserstatus"){
mule.registerPool(poolObj);
}
} else {
log.error("No action on poolObj while attempting to calculate the need for a registerPool call");
}
}
}
// parse data
callback.apply(this, data);
},
error : function(x,h,r){ ... },
dataType : 'json'
});
},
sendApiRequest : function(poolObj,action,callback){
var url = poolObj.url + '&page=api&action=' + action;
var $poolOut = constants.cache.monitorOutput.find('.pool-out.' + poolObj.id);
var dfd = mpos.sendMessage(url,callback,$poolOut);
$.when(dfd).always(function(){
var refreshTimer = setTimeout(function(){
if(constants.state.monitorEnabled){
mpos.sendApiRequest(poolObj, action, callback);
}
}, poolObj.refreshRate);
});
},
msgs : {
"getuserstatus" : function(data){ ... },
"getpoolstatus" : function(data){ ... },
"getuserworkers" : function(data){ ... },
"getuserbalance" : function(data){ ... }
}
};
return mpos;
}
);
Thanks!
NOTE: I am assuming that $poolOut.data('poolObj') is being used to find the poolObj instance passed in the call to startMonitoring, and will return the same instance each time.
You state, "Each iteration of the loop fires off a different request with different arguments being passed to module B's function that fires off the Ajax request."
This statement is not correct. Each iteration fires off a different request with the first argument poolObj being the same in each iteration.
In your .each iteration, you are overwriting the value of poolObj.action before each call to sendApiRequest.
In the AJAX success handler, which is likely invoked after all iterations have completed, the value of poolObj.action will have the value you set it to in the last iteration.
To solve this, I think you need to pass action as a parameter to sendMessage, too, so that a separate value is being stored in the closure for each function call.
var mpos = {
sendMessage : function(postData,action,callback,$poolOut){
return $.ajax({
'type':'post',
'url':constants.URLS.proxy,
'data':{'url':postData},
success : function(data){
// if we have $poolOut we know this is a mpos call
if($poolOut != undefined){
var keys = Object.keys(data);
// add poolOut to data
data.poolOut = $poolOut;
var poolObj = $poolOut.data('poolObj');
if(poolObj){
// action is not guaranteed to be the same as poolObj.action here,
// since poolObj.action may have changed since this function was first called
console.log(poolObj,action);
if(action){
if(action == "getuserstatus"){
mule.registerPool(poolObj);
}
} else {
log.error("No action on poolObj while attempting to calculate the need for a registerPool call");
}
}
}
// parse data
callback.apply(this, data);
},
error : function(x,h,r){ ... },
dataType : 'json'
});
},
sendApiRequest : function(poolObj,action,callback){
var url = poolObj.url + '&page=api&action=' + action;
var $poolOut = constants.cache.monitorOutput.find('.pool-out.' + poolObj.id);
var dfd = mpos.sendMessage(url,action,callback,$poolOut);
$.when(dfd).always(function(){
var refreshTimer = setTimeout(function(){
if(constants.state.monitorEnabled){
mpos.sendApiRequest(poolObj, action, callback);
}
}, poolObj.refreshRate);
});
},
msgs : {
"getuserstatus" : function(data){ ... },
"getpoolstatus" : function(data){ ... },
"getuserworkers" : function(data){ ... },
"getuserbalance" : function(data){ ... }
}
};
I solved a synchronous problem with ajax that way to use deferred function.
I get as an response now big object with statusResponse etc etc inside.
My JSONP Objects are also successfully stored in responseJSON Object. How Can I extract it or save it in another variable ?
var web = new webService();
web.init(app.info);
and here the class
function webService() {
this.init = function(app) {
var d = this.connect(app).done(function(){});
console.log(d);
}
this.connect = function(app) {
console.log(app);
return $.ajax({
url: 'working url',
dataType: 'jsonp',
jsonp: 'jsoncallback',
timeout: 5000
});
}
}
.done() is going to be called when the data has returned (but through a parameter). So, make sure you add a parameter to your callback function then inspect it for whatever you want out of it.
this.connect(app).done(function(mydata){
// mydata = the response object
// grab whatever information you want from it here.
});
I'm trying to extend the Ext.Container object within the Sencha Touch framework to add a few extra properties I need, one of which is loading a file through AJAX.
App.Ext.AContainer = Ext.extend(Ext.Container, {
ajax : false,
doAjax : function(p, that) {
Ext.Ajax.request({
url : p,
method : 'POST',
success : function(result) {
Ext.apply(that, {
html : result.responseText
})
},
failure : function(result) {
Ext.Msg.alert('ERROR : AJAX : Could not load ' + p);
}
})
},
constructor : function(b) {
if( b.ajax === true && b.hasOwnProperty('rhtml')) {
this.doAjax(b.rhtml, this);
}
App.Ext.AContainer.superclass.constructor.call(this, b);
console.log(this);
}
});
With the actual implementation of that Container being :
var servicesContainer = new App.Ext.AContainer({
scroll : 'vertical',
ajax : true,
rhtml : 'test.html'
});
Basically, my idea was have an method that takes care of loading the file and then copy it to the html property manually. When I check the console from outputting 'this', it shows that the html property is getting set with the correct markup, but it doesn't render the markup to the page.
Not really sure what I'm doing wrong.
Look at the if inside your constructor. It calls doAjax, that in turn calls Ext.Ajax.request with the success function in its argument. The request function documentation says:
Important: Ajax server requests are asynchronous, and this call will
* return before the response has been received. Process any returned data
* in a callback function.
This means that instantly the control flow goes back to your constructor, before the load completes (I guess even before the request is sent).
So, at the time console.log is called, the request (and the success function) isn't executed yet.
TL;DR:
Move the console.log call to the success function.
Also, there's no need to pass the scope to doAjax, instead:
App.Ext.AContainer = Ext.extend(Ext.Container, {
ajax: false,
doAjax: function (p) {
Ext.Ajax.request({
url: p,
method: 'POST',
scope: this,
success: function (result) {
this.html = result.responseText;
},
failure: function (result) {
Ext.Msg.alert('ERROR : AJAX : Could not load ' + p);
}
})
},
constructor: function (b) {
if (b.ajax === true && b.hasOwnProperty('rhtml')) {
this.doAjax(b.rhtml);
}
App.Ext.AContainer.superclass.constructor.call(this, b);
console.log(this);
}
});