How to execute jQuery AJAX calls in order? - javascript

I have an input field. Whenever there's change in the text in the input field, I make an ajax call to process.php
I need to handle all the responses. But some responses come early, whereas some come late, depending on the input. So the order of responses is not same as order of making ajax calls.
So right now I'm doing it by setting async: false
But I don't want the client side to be stuck due to async: false
$("#text_input").on("input", function() {
var password = $("#text_input").val();
var length = password.length;
$.ajax({
url: "process.php",
type: "POST",
async: false,
data: {
password: $(this).val()
}
}).done(function (data) {
console.log(data);
console.log(password + " : " + length);
}).fail(function( jqXHR, textStatus, errorThrown ) {
alert( "Request failed: " + textStatus + " , " + errorThrown );
});
});
I tried looking in promises, but did not understand whether it can be applied here.
How can I execute responses in order?

The problem with async is exactly that, that is asynchronous, that means that the code runs without waiting to finish. Therefore all your requests go to the server and once they start returning your code catches them and executes them.
If you want to handle them in order, you will need to build a queue and the code to handle the queue.
You should then assign an ordered number to all your requests which should then come in the response (so you know the proper order of the response).
Then you can add the response to the queue, and call a method which processes the queue, but that method only processes the queue in order, meaning that it only processes responses starting from 0, 1, 2, 3 etc... so if in the queue there is a result 5 and not a result 0, the queue won't be processed, the queue will be processed only if result 0 is there and so on...
Here is an example code, I haven't tested it but should work or at least should give you an idea on how to start :)
var currentOrder = 0;
var queue = [];
function processQueue() {
// Sort the queue
queue.sort(function(a, b) {
var aOrder = a.order;
var bOrder = b.order;
if (aOrder < bOrder) {
return -1;
}
if (aOrder > bOrder) {
return 1;
}
// Order is equal (this shouldn't happen)
return 0;
});
if (queue[0].order === currentOrder) {
doSomething(data);
queue.splice(0, 1); // Remove the first item from the queue as it's already processed
currentOrder++;
processQueue(); // Process the queue again
}
}
// Do something with the data
function doSomething(data) {
console.log(data);
}
$('#text_input').on('input', function() {
$.ajax({
url: 'process.php?order=' + order, // We send the order to the backend so it can be returned in the response
type: 'POST',
data: {
password: $(this).val()
}
}).done(function(data) {
// Data should contain the order somewhere, let's say for example it's a json and it's inside data.order
queue.push(data);
processQueue();
});
});

Assuming that you are only interested in the results of the latest ajax call, you can assign the value returned from the $.ajax call - a jqXHR object - to a variable and then abort the request when a new one is fired.
Something like:
var request = null;
$("#password_input").on("input", function() {
var password = $("#password_input").val();
var length = password.length;
// you can also check the type of the variable, etc.
if (request) {
request.abort();
}
request = $.ajax({
url: "process.php",
type: "POST",
...

Why not use setTimeout function to whenever they press some letter on your input field. you can do it just like this:
setTimeout(function(){
$.ajax({
url: "process.php",
type: "POST",
data: {
password: $(this).val()
}
}).done(function (data) {
console.log(data);
console.log(password + " : " + length);
}).fail(function( jqXHR, textStatus, errorThrown ) {
alert( "Request failed: " + textStatus + " , " + errorThrown );
});
}, 1000);
This is to setTimeout once the press on your input field and wait 1sec then when finished type on the input field it will generate the ajax after 1sec.

Related

Javascript object counter not incrementing as expected

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.

When one ajax is SUCCESS load next

I have been looking into a jQuery Ajax queue system. I have a step by step generator. It generates a pdf and then once the pdf is generated an image is created. Once these 2 processes are complete I then send an email confirmation. It must also be flexible to add additional steps.
However, I have yet to find an example that works. They all use 'COMPLETE' rather than 'success' so if I return an error via jSON then it is ignored. It moves on to the next in the queue
Any ideas?
EDIT
It's quite complex whats happening.
My plugin (copied from another plugin)
$.AjaxQueue = function() {
this.reqs = [];
this.requesting = false;
};
$.AjaxQueue.prototype = {
add: function(req) {
this.reqs.push(req);
this.next();
},
next: function() {
if (this.reqs.length == 0)
return;
if (this.requesting == true)
return;
var req = this.reqs.splice(0, 1)[0];
var complete = req.complete;
var self = this;
if (req._run)
req._run(req);
req.complete = function() {
if (complete)
complete.apply(this, arguments);
self.requesting = false;
self.next();
}
this.requesting = true;
$.ajax(req);
}
};
I have also written a function to speed my code up
function createQueue(file, inputid, step, params) {
var queue = new $.AjaxQueue();
queue.add({
url: file,
type: 'POST',
dataType: "json",
data: params,
complete : function(data, status) {
$('li#step' + step + ' .loading').remove();
// DO SOMETHING. CANT CHECK FOR ERRORS
},
success : function(data, status) {
// DOES NOT WORK
},
error: function(xhr, desc, err) {
console.log(xhr);
console.log("Details: " + desc + "\nError:" + err);
},
_run: function(req) {
//special pre-processor to alter the request just before it is finally executed in the queue
//req.url = 'changed_url'
$('li#step' + step).append('<span class="loading"></span>');
}
});
}
Step 1. I am using mpdf to generate a pdf. Now this takes a few seconds to actually build depending on theme, images used etc. So i call this:
createQueue('post_pdf.php', id, 1, { 'filename': filename + '.pdf', 'id': id, 'crop': crop } );
Step 2 - Generate some images
createQueue('ajax_image.php', id, 2, { 'filename': filename + '.pdf' } );
Step 3 - (something else like send email summary)
createQueue('mail.php', id, 3, { 'from': 'newfilename', 'to': 'emavle#pb.com', 'subject': 'This is a subject', 'body': 'Body Copy' } );
If it fails at step 1 I can see it in console but its not returned
As #charlietfl suggested, have each step in PHP on server side. After the AJAX call is done, you can have the response from the server and continue based on that. Example:
// make AJAX request to file.php and send 'data'
var request = $.ajax({
url: "file.php",
type: "POST",
data: { data }
});
// when PHP is done, receive the output and act accordingly
request.done(function( msg ) {
if (msg == "A") {
// plan A
} else if (msg == "B") {
// plan B
}
});

How to deal with a group of deferred's when one of them fails

I have a series of ajax calls that fill columns on a page.
var doneDefers = function(defer) {
// leftColDefer is another deferred that sets some header info
$.when(defer, leftColDefer).done(function(req1, req2){
var data = req1[0],
head = req2[0];
// spit this data out to elements on the page
});
};
for(i=0;i<window.ids.length;i++){
defer[i] = $.ajax({
url: 'api/get_runs_stats.php',
type: 'GET',
dataType: 'json',
data: {
run_id: window.ids[i]
}
});
doneDefers(defer[i]);
}
This works fine. If an ajax call fails, nothing is spit out and all is right with the world.
Now I want to do some calculations based on all the data that got spit out.
$.when.apply(null, defer)
.done(function() {
var args = Array.prototype.slice.call(arguments);
calcDeltas();
})
.fail(function() {
var args = Array.prototype.slice.call(arguments);
console.log('in list fail');
});
The done function works fine none of the ajax calls fail. If one of them fail, I go into the fail function and I don't have access to any of the return data from the other runs. The arguments array only has the failed call's data.
I would like to do my calculations on the data sets that passed. How can I get to the data from the good calls when one of them fails?
I'm not sure this is the simplest solution but it stands a chance of working.
var ajax_always_promises = [],//to be populated with promises that (barring uncaught error) are guaranteed to be resolved.
data_arr = [],//an array to be (sparsely) populated with asynchronously delivered json data.
error_arr = [];//an array to be (sparsely) populated with ajax error messages.
$.each(window.ids, function(i, id) {
var dfrd = $.Deferred();
var p = $.ajax({
url: 'api/get_runs_stats.php',
type: 'GET',
dataType: 'json',
data: {
run_id: window.ids[i]
}
}).done(function(json_data) {
data_arr[i] = json_data;//intentionally not `data_arr.push(json_data);`
}).fail(function(jqXHR, textStatus, errorThrown) {
error_arr[i] = textStatus;//intentionally not `error_arr.push(textStatus);`
}).always(dfrd.resolve);
ajax_always_promises[i] = dfrd.promise();
doneDefers(p);
});
$.when.apply(null, ajax_always_promises).done(function() {
//The data in the (sparsely) populated arrays `data_arr` and `error_arr` is available to be used.
var i, id, success_count=0, error_count=0;
for(i=0; i<Math.max(data_arr.length,error_arr.length); i++) {
//Here, the index i corresponds to the original index of window.ids ...
//...that's the advantage of sparsely populating the arrays.
id = window.ids[i];
if(data_arr[i]) {
//Here, do whatever is required with `data_arr[i]`, and `id` if needed.
success_count++;
}
else if(error_arr[i]) {
//Here, do whatever is required with `error_arr[i]`, and `id` if needed.
error_count++;
}
}
console.log("Success:errors: " + success_count + ':' + error_count);
});
Untested - may well need debugging

Method POST, Status (canceled) error message

I have the following code which is giving me a Method POST, Status (canceled) error message:
$(document).ready(function() {
var xhr = false;
get_default();
$('#txt1').keyup( function() {
if(xhr && xhr.readyState != 4){
alert("abort");
xhr.abort();
}
if ($("#txt1").val().length >= 2) {
get_data( $("#txt1").val() );
} else {
get_default();
}
});
function get_data( phrase ) {
xhr = $.ajax({
type: 'POST',
url: 'http://intranet/webservices.asmx/GetData',
data: '{phrase: "' + phrase + '"}',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function( results ) {
$("#div1").empty();
if( results.d[0] ) {
$.each( results.d, function( index, result ) {
$("#div1").append( result.Col1 + ' ' + result.Col2 + '<br />' );
});
} else {
alert( "no data available message goes here" );
}
},
error: function(xhr, status, error) {
var err = eval("(" + xhr.responseText + ")");
alert(err.Message) ;
}
});
}
function get_default() {
$('#div1').empty().append("default content goes here.");
}
});
The code actually works as long as each ajax request completes, but if I type fast into txt1, i.e. type the next character before the previous request finishes, I get the error message Method POST, Status (canceled).
Anyone know why this is happening and how to correct the error?
I suppose that the problem is very easy. If you call xhr.abort(); then the error callback of $.ajax will be called for the pending request. So you should just ignore such case inside of error callback. So the error handler can be modified to
error: function(jqXHR, textStatus, errorThrown) {
var err;
if (textStatus !== "abort" && errorThrown !== "abort") {
try {
err = $.parseJSON(jqXHR.responseText);
alert(err.Message);
} catch(e) {
alert("ERROR:\n" + jqXHR.responseText);
}
}
// aborted requests should be just ignored and no error message be displayed
}
P.S. Probably another my old answer on the close problem could also interesting for you.
That is because you are calling abort method which possibly triggers the error handler with appropriate error message.
You can possibly wait for previous ajax request to complete before making the next call.
In order to both fix your problem and save on the amount of Ajax calls I have written the following example. This example allows you to handle the following two situations:
Situation 1:
The user types slow enough (lets say about one key every 200+ miliseconds
Situation 2:
The user types fast (my average is about 20 to 50 miliseconds per key)
In the following example there is no need to abort or ignore Ajax calls, you are not spamming Ajax calls and you are using an Object to handle your job. (I even jsFiddled it for you)
var Handler = {
/**
* Time in ms from the last event
*/
lastEvent: 0,
/**
* The last keystroke must be at least this amount of ms ago
* to allow our ajax call to run
*/
cooldownPeriod: 200,
/**
* This is our timer
*/
timer: null,
/**
* This should run when the keyup event is triggered
*/
up: function( event )
{
var d = new Date(),
now = d.getTime();
if( ( now - Handler.lastEvent ) < Handler.cooldownPeriod ) {
// We do not want to run the Ajax call
// We (re)set our timer
Handler.setTimer();
} else {
// We do not care about our timer and just do the Ajax call
Handler.resetTimer();
Handler.ajaxCall();
}
Handler.lastEvent = now;
},
/**
* Function for setting our timer
*/
setTimer: function()
{
this.resetTimer();
this.timer = setTimeout( function(){ Handler.ajaxCall() }, this.cooldownPeriod );
},
/**
* Function for resetting our timer
*/
resetTimer: function()
{
clearTimeout( this.timer );
},
/**
* The ajax call
*/
ajaxCall: function()
{
// do ajax call
}
};
jQuery( function(){
var field = jQuery( '#field' );
field.on( 'keyup', Handler.up );
});
Hope this helps.
You are using the keyup event, which seems to be the problem.
If anything at all, you need to wait after typing one character before taking action.
A better solution might be to follow the same strategy as the JQuery AutoComplete COmponent.
Ajax is an async type, its not recommonded that u to send request on every keyup event, try the...
async: false
in post method... it'll pause the subsequent posts until the current request done its callback
Realistically you need a setTimeout method in order to prevent redundant ajax calls being fired.
clearTimeout(timer);
if($("#txt1").val().length >= 2){
timer = setTimeout(function(){
get_data($("#txt1").val());
}, 400);
}else{
get_default();
}
This should eradicate your problem.

my function can't pass it's parameter value javascript

Ive been struggling to pass my parameters from a functions but I just really can't figure out where did I go wrong. I have a function that have a parameters that I want to pass to my postData to display datas in my jQgrid. Here's my function code with parameters:
function getTID(hdrID){
var selected = $('#editTallyHdr').val();
var hdrID = '';
var hdrNo = '';
var nameFlag=0;
var par_ams = {
"SessionID": $.cookie("SessionID"),
"dataType": "data"
};
$.ajax({
type: 'GET',
url: 'processjson.php?' + $.param({path:'getData/tallyHdr',json:JSON.stringify(par_ams)}),
dataType: primeSettings.ajaxDataType,
success: function(data) {
if ('error' in data)
{
showMessage('ERROR: ' + data["error"]["msg"]);
}
else{
$.each(data['result']['main']['rowdata'], function(rowIndex, rowDataValue) {
$.each(rowDataValue, function(columnIndex, rowArrayValue) {
var fldName = data['result']['main']['metadata']['fields'][columnIndex].name;
if (fldName == 'transaction_id'){
hdrID = rowArrayValue;
}
if (fldName == 'transaction_num'){
hdrNo = rowArrayValue;
if(selected == hdrNo){
nameFlag =1;
};
}
});
});
}
}
});
return (hdrID);
}
and here is my jQgrid code where I call that function to get it's parameter:
$("#tblPlank").jqGrid({
url: '',
datatype: 'local',
jsonReader : {
.
.
.
serializeGridData: function(postData) {
var ctr =0;
var filt=[];
var c=[];
var jsonParams = {
'SessionID': $.cookie("SessionID"),
'dataType': 'data',
'transaction_id':getTID(hdrID),
'filters': c,
'lines':plank_data,
'recordLimit': postData.rows,
'recordOffset': postData.rows * (postData.page - 1),
'rowDataAsObjects': false,
'queryRowCount': true,
'sort_fields': postData.sidx
};
.
.// some code here
.
.
return 'json=' + JSON.stringify(jsonParams);
},
loadError: function(xhr, msg, e) {
showMessage('HTTP error: ' + JSON.stringify(msg) + '.');
},
colNames:[...],
colModel:[
........................
],
.
.
.
caption: "Tally Transaction Details/Lines"
I also have another code where I want to get that parameter. Here's the last code:
var par_ams = {
"SessionID": $.cookie("SessionID"),
"dataType": "data",
"transaction_id": getTID(hdrTID)
}
$('#tblPlank').setGridParam({
url:'processjson.php?path=' + encodeURI('getData/tallyLnDtl') + '&json=' + encodeURI(JSON.stringify(par_ams)),
datatype: primeSettings.ajaxDataType,
});
$('#tblPlank').trigger('reloadGrid');
Those codes below that function getTID(hdrID) cant retrieve the parameter, it shows empty. This maybe simple to anyone, but I really need help on this.. been working with this for quite long hours.
This is a very common misunderstanding. I've probably answered 15 of these questions in the last two weeks alone. An ajax call is an asynchronous call. That means that when you make the ajax call, it just STARTs the request. Then, while that request goes in the background, your code immediately keeps executing. That means that your function getTID() returns before the ajax call has even completed and it's value is not yet known. Thus, there is no way to return the response value from the ajax function when you return from getTID() as it is simply not known yet.
To work with asynchronous function calls (like ajax calls), you have to change your programming style to something that works asynchronously. In this case, the response to your ajax call is ONLY known in the success handler for the ajax all. So, you have to restructure your code to continue on with the execution of your processing and the handling of the ajax response from the success handler. If you have only a little bit of work to do, then you can put it all in the success handler. If you have a lot of work to do, then you can put all the rest of that work in a function call and call it from the success handler.
The problem is that you're doing an ajax-request (asynchronous request). Then the function does not wait for an answer to arrive, but just continues and returns hdrID (which isn't set at the time). After that a response comes in, and the success-method is called, which sets hdrID to the appropiate value.
The common way to solve this is to execute a specific function with the desired values when the success-method is executed. It's too much code to look into, but it could go something like this:
function fetchContent(continueFunction) {
$.ajax(params).success(function(reply) {
// retrieve desired params from reply
continueFunction(retrievedParameters);
}
}
What you could do is define getTID to take in a callback to execute once it has the id, for instance
function getTID(hdrID, callback) {
//ajax stuff....
success: function (data) {
// Error checks, etc
hdrID = //something dependent on data
callback(hdrID); // THIS IS THE IMPORTANT PART
}
the callback will execute after the request has returned, when it is safe to use the data returned from the ajax request that will be needed in the callback. You could wrap all of the code that needs the return value of the request in the callback, for example
getTID(hdrID, function (ID) {
var params = {
"SessionID": $.cookie("SessionID"),
"dataType": "data",
"transaction_id": ID //USE ID
}
$('#tblPlank').setGridParam({
url:'processjson.php?path=' + encodeURI('getData/tallyLnDtl') + '&json=' + encodeURI(JSON.stringify(par_ams)),
datatype: primeSettings.ajaxDataType,
});
$('#tblPlank').trigger('reloadGrid');
};
});

Categories