I have written a custom js module that basically sends messages and needs to wait for a response in order to continue:
var manageBooking = (function (jQ) {
//decalre private variables
var domain, msgRecieved, msgResponse, validationValue;
//decalre private functions
var sendMessage, wait;
// A private variables
domain = document.domain;
msgRecieved = false;
msgResponse = null;
wait = function(timeOutStep){
var w;
console.log('msgRecieved', msgRecieved);
if (msgRecieved === true) {
clearTimeout(w);
return;
} else {
console.log('waiting..');
w = setTimeout(wait, timeOutStep, timeOutStep);
}
}
// A private function to send messages
sendMessage = function( requestURL, data, type ) {
console.log(requestURL);
console.log(data);
console.log(type);
//reset vars to defaults
msgRecieved = false;
msgResponse = null;
jQuery.ajax({
url: "http://"+domain+"/_ajax/"+requestURL,
dataType: "html",
async: true,
data: data,
type: type,
success: function(msg){
console.log(msg);
msgResponse = msg;
msgRecieved = true;
}
});
console.log('after ajax call');
wait(500);
console.log('after wait');
console.log('msgRecieved', msgRecieved);
return;
};
return {
// A public variable
errorMsg: "",
validationName: "",
bookingID: "",
output: "",
// A public function to login
login: function( enteredBookingID, enteredSurname ) {
// Call private sendMsg
sendMessage("user_login/"+enteredBookingID+"/"+enteredSurname, null, 'GET');
console.log(msgResponse);
throw "error";
//check response
var patt=/Sorry/i;
//test pattern
var result=patt.test($.trim(msgResponse));
//if false OK
if (result === false) {
var split = msgResponse.split('|');
validationName = split[0];
validationValue = split[1];
bookingID = enteredBookingID
return true;
}
//else error
errorMsg = msgResponse;
return false;
}
};
})(jQuery);
manageBooking.login(123,123);
The issue i am having is forcing the sendMessage function to wait until the ajax completes and sets msgRecieved to true.
However it appears that the sendMessage function hits the wait function once and then continues.
the following console output shows the order of events:
GET http://website/_ajax/user_login/123/123
after ajax call //sendMessage()
msgRecieved, false //wait()
waiting.. //wait()
after wait //sendMessage()
msgRecieved, false //sendMessage()
null//login()
uncaught exception: error //login()
<p>Sorry, we cannot locate your details. </p> <!-- jQuery Ajax call -->
msgRecieved, true //wait()
What I am confused with is that the wait function seems to fire again right at the end..
can anyone give me some pointers on getting this to work?
JavaScript behaves in an asynchronous manner, meaning it does not wait.
You have a part in your code that looks like this:
jQuery.ajax({
url: "http://"+domain+"/_ajax/"+requestURL,
dataType: "html",
async: true,
data: data,
type: type,
success: function(msg){
console.log(msg);
msgResponse = msg;
msgRecieved = true;
}
});
You should place the code to be run when the response arrives within the success function, like so:
success : function (msg) {
handleMessage(msg); // Or any other manipulation to the received message
}
function handleMessage(msg) {
// Work with your received message here.
}
success will be called with the received message, it is a callback.
The right way to implement sendMessage would be the following way:
sendMessage = function( requestURL, data, type, callback ) {
console.log(requestURL);
console.log(data);
console.log(type);
//reset vars to defaults
msgRecieved = false;
msgResponse = null;
jQuery.ajax({
url: "http://"+domain+"/_ajax/"+requestURL,
dataType: "html",
async: true,
data: data,
type: type,
success: function(msg){
console.log(msg);
msgResponse = msg;
msgRecieved = true;
// Call the callback function to notify the message
// was received
callback();
}
});
};
and then using it like so:
sendMessage(urlHere, dataHere, typeHere, function () {
// Message has been received, msgResponse and msgReceived
// have already been updated. Do what you need here
});
the problem may be related to the scope of your w variable, because on second call (in the wait function, into your else branch) you're destroying the reference to the timeout you previously created, so the clearTimeout can't work: try to define it in the immediate outer scope.
You should try using the JavaScript setInterval function instead of setTimeout. But this time, break up sendMessage, and place the part that needs to execute after the ajax message is received under a setInterval.
Once the ajax message is received this second part of sendMessage runs (after messagereceived is true), and also clears the interval.
This is since setTimeout() only executes once after a set interval.
setInterval() executes repeatedly exery interval until it is cleared.
More information can be found Here
Hope this helps!
Related
I have a timing or scope problem on this function call.. or no idea what.
AjaxHandlerByClass('url', {clientName: this.clientName}, function (response) { this code gets never called})
AjaxHandlerByClass('url', {clientName: this.clientName}, function (response) { This code gets called 2 times})
From this Function
function AjaxHandlerByClass(className, postData, callback, callbackFail) {
var timestamp = new Date().getTime();
var me = this;
me.backHandler = function (data) {
if (data)
if (data.responseJSON || data.debug) {
if (data.debug)
var debug = data.debug;
else if (data.responseJSON && data.responseJSON.debug)
var debug = data.responseJSON.debug;
if (window.console) {
for (var key in debug) {
if (debug.hasOwnProperty(key)) {
// console.log(debug[key]);
}
}
}
}
if (me.mode = 'callback') {
callback(data); //<--- this is the bug location
} else {
callbackFail(data);
}
};
this.ok = function (data) {
me.mode = 'callback';
me.backHandler(data)
}
this.notOk = function (data) {
me.mode = 'callbackFail';
me.backHandler(data)
}
$.ajax(
{
contentType: "application/json; charset=utf-8",
url: className + '?ts=' + timestamp + '&sid=' + sid,
type: 'post',
data: JSON.stringify(postData),
dataType: 'json',
cache: false,
success: me.ok,
error: me.notOk
}
);
}
The first callback Function never gets executed, while the second one does get executed but 2 times.
The bug happens on the if (me.mode = 'callback') part of the code.
I already tried other options to make the callback function stuck right.
The first attempt was to store the callback function in the Function scope itself.
with assigning it to this.callback and then trying to access it via me.scope
which did not work. then I tried to access the variables directly.. and it is not helping either...
this.callback = callback;
this.callbackFail = callbackFail;
var me = this;
me.backHandler = function (data) {
if (data)
if (data.responseJSON || data.debug) {
if (data.debug)
var debug = data.debug;
else if (data.responseJSON && data.responseJSON.debug)
var debug = data.responseJSON.debug;
if (window.console) {
for (var key in debug) {
if (debug.hasOwnProperty(key)) {
// console.log(debug[key]);
}
}
}
}
me[me.mode](data);
};
I'm on my wit's end.
First of, callBackFail is never defined in your code.
function AjaxHandlerByClass(className, postData, callback, callbackFail)
You pass in three parameters: className, postData and callback.
AjaxHandlerByClass('url', {clientName: this.clientName}, function (response) { this code gets never called})
Second, this line should be me.mode === 'callback', not me.mode = 'callback'
if (me.mode === 'callback') {
callback(data);
} else {
callbackFail(data);
}
You've named your function AjaxHandlerByClass, I assume you want to use it as a class. You've declared it as function. Regular functions are executed when they are invoked(called) causing the second call to AjaxHandlerByClass() to render twice. To solve your problem you could either create a new instance of your AjaxHandlerByClass using the new keyword.
const firstRequst = new AjaxHandlerByClass('url', {clientName: "https://swapi.co/api/people/"}, successCallback, failCallback);
const secondRequst = new AjaxHandlerByClass('url', {clientName: "https://swapi.co/api/planets/"}, successCallback, failCallback);
Or if you want to wait for the first request to finish before calling the second request you could implement Promise. More on Promise here.
I created a js-fiddle here with some modifications(swapped api and renamed some varaibles just for testing purpose.). One of the ajax-request is successful and the other fails. The result is visible in the developer-console. Note this fiddle is not perfectly written, its just some dummy code for demo purpose.
I am having trouble getting the errorCount property to increase during code execution. The problem I am having is occurring inside of the $.ajax request, more specifically the addError() method.
If I use the following code below to check the current count of errorCount it always returns 0 even though I have manually created an error to occur. But inside of the ajax method after I call addError() and then check the value of errorCount it shows 1 like it should. What did I do wrong?
var boom = new test(settings, formData, search);
console.log(boom.errorCount);
boom.queueCalls(settings);
console.log(boom);
console.log(boom.errorCount);
Here is the object code:
function test(a, b, c) {
this.settings = a;
this.formData = b;
this.search = c;
this.errorCount = 0;
}
test.prototype = {
constructor: test,
queueEmails:function(settings, formData, search) {
var url = '/example-url-endpoint';
var data = {postData: settings + "&" + formData + "&" + search};
this.sendRequest(url, data);
},
queueCalls:function(settings) {
var url = '/example-url-endpoint2';
this.sendRequest(url, settings);
},
addMessage:function(response) {
flashMessage(response.Message, response.Result);
},
addError:function() {
this.errorCount++;
},
sendRequest:function(url, data) {
var blah = this;
j$.ajax({
type: "POST",
url: url,
data: data,
dataType: 'json',
success: function(data) {
response = JSON.parse(data);
if(response.Result != 'error') {
blah.addMessage(response);
} else {
blah.addMessage(response);
blah.addError();
console.log(blah.errorCount);
}
},
error: function(e, textStatus, errorThrown) {
blah.addError();
console.log(blah.errorCount);
alert("There was an error creating the queue");
}
});
}
}
The problem is you are doing an asynchronous (AJAX) call. When you call the queueCalls function, it makes the AJAX call, then runs your console.log statements. It does not wait until the AJAX call is done, and you have received your errors to run the console statements. If you want to do that, look at the jQuery documentation for .ajax(), and either make your AJAX call not asynchronous, or put your console statements in a .done() event handler that will fire after the AJAX call is complete.
I'm making a jquery library to use an application with the json rpc protocol but I'm stuck with a little problem.
This is the fiddle that shows the code (obviously it can't work): https://jsfiddle.net/L9qkkxLe/3/.
;(function($) {
$.lib = function(options) {
var outputHTML = [],
plugin = this;
var APIcall = function(api_method, api_params) {
request = {};
request.id = Math.floor((Math.random() * 100) + 1);
request.jsonrpc = '2.0';
request.method = api_method;
request.params = (api_params) ? api_params : [];
$.ajax({
type: "POST",
url: "http://localhost:8898/jsonrpc",
data: JSON.stringify(request),
timeout: 3000,
beforeSend: function(xhr) {
xhr.setRequestHeader('Authorization', window.btoa(options.username + ":" + options.password));
},
success: function(data) {
handleData(data, api_method);
},
error: function(jqXHR, textStatus, errorThrown) {
log("Connection time out: can't reach it. Try changing the settings.");
isConnected = "false";
},
dataType: "json"
});
}
var handleData = function(data, method) {
if (method == "getgenres") {
outputHTML = data.result.genres; //I need data.result.genres to return in getgenres function
}
}
var log = function(msg) {
if (options.debug == true) console.log(msg);
}
plugin.getgenres = function() {
APIcall("getgenres");
return outputHTML; //This is sadly empty.
}
};
}(jQuery));
var init = new $.lib();
console.log(init.getgenres());
I need that the getgenres function returns data.result.genres but actually it returns an empty array because getgenres is called for first and only after the handleData function gives to outputHTML the value that I need.
You are performing an asynchronous AJAX request, which means you can't actually get back the data immediately. There are two ways to solve your issue: making it synchronous (easy but ill advised) or using a callback (a little bit more complex but generally accepted):
In your getgenres function, you could accept one more parameter: callback
plugin.getgenres = function(callback) {
/* Dont forget APIcall already took two parameters in, so callback has to be the third in line! */
APIcall("getgenres", false, callback);
}
Now modify your APIcall function to accept your callback:
var APIcall = function(api_method, api_params, callback) { ... }
And call the callback from the successful completion call - instead of having a handler method in between wrapped in a function, you can simply pass the anonymous function. So instead of success: function(data){ handle(data); }, just use:
success: callback
The anonymous function that we will pass to it will receive as its first parameter the data you were passing to the handler. Now you can do the following:
var myGenres = [];
var init = new $.lib();
init.getgenres(function(data){
/* Now your data is actually loaded and available here. */
myGenres = data;
console.log(myGenres);
});
I would like to point out that there are many better ways to handle this, including turning this into a Constructor (More here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) instead of the strange amalgamation of functions and variables you have now, as well as using JS Promises (here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) to make this easier. But the basic gist should be here.
Update (potential implementation)
Because I mentioned that this could be done in a way that I think is clearer to read and use. I do not know all use cases for this, but from the provided example I would change the code to something looking like the following. Please also note I am not an expert on jQuery plugins, so I am avoiding plugging into jQuery and just using it as an easy AJAX call.
function getAjax(){
if(!window.jQuery || !window.$) throw("jQuery is required for this plugin to function.");
this.data = [];
this.request = '';
return this;
}
getAjax.prototype = {
createRequest : function(method, parameters){
this.request = {};
this.request.id = Math.floor((Math.random() * 100) + 1);
this.request.jsonrpc = '2.0';
this.request.method = method;
this.request.params = parameters || [];
return this;
},
callRequest : function(options, callback, error){
var self = this;
// We could also `throw` here as you need to set up a request before calling it.
if(!this.request) return this;
else {
$.ajax({
// We will allow passing a type and url using the options and use sensible defaults.
type: options.type || "POST",
url: options.url || "http://localhost:8898/jsonrpc",
// Here we use the request we made earlier.
data: JSON.stringify(this.request),
timeout: options.timeout || 3000,
beforeSend: function(xhr){
xhr.setRequestHeader(
'Authorization',
window.btoa( options.username + ":" + options.password)
);
},
// We will also store all the made request in this object. That could be useful later, but it's not necessary. After that, we call the callback.
success: function(data){
var store = {request:self.request, data: data};
self.data.push(store);
// Call the callback and bind `this` to it so we can use `this` to access potentially pther data. Also, pass the results as arguments.
callback(data, self.request.id).bind(self);
},
// Error function!
error: error,
dataType: options.dataType || "json"
});
}
return this;
}
}
// Example use
new getAjax().createRequest('getgenres').callRequest({
username: 'myusername',
password: 'mypassword'
}, function(data, id){
// Success! Do with your data what you want.
console.log(data);
}, function(e){
// Error!
alert('An error has occurred: ' + e.statusText);
console.log(e);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
What I do in those occasions is this:
You are supplying a method. So put a reference to the a callback function. In this case plugin.getGenresFinalize. When handleData is called it will fire that callBack function. This way you can pass multiple methods to the api call for different types of data.
plugin.getgenres = function() {
APIcall(this.getgenresFinalize);
}
plugin.getgenresFinalize = function(data) {
console.log(data);
}
var handleData = function(data, method) {
method(data);
}
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