I have what might be a tricky question.
I am working on a form where it verifies a couple things on submit, using event.preventDefault(); to prevent the form from submitting if something went wrong. The issue here is that it sends multiple ajax requests at the same time, which seems to stop the php (which is processing the AJAX call) from modifying the $_SESSION variable.
I have determined this by changing the jquery ajax calls to process synchronously, allowing the $_SESSION variable to be changed.
My question is this: is there a way to allow the ajax calls to happen synchronously while allowing the $_SESSION variable to be modified during the process of those calls? I realize that the async:false for an AJAX call is deprecated, and obviously not the best solution.
Due to what each call does, it is not possible to combine the functionality of these two calls, although each call does not take long at all to process.
Example jquery code to explain how I am making these AJAX calls (some redaction and simplification, obviously):
$("#form-id").on('submit', function(event) {
$.ajax({
type: 'POST',
url: '/url/to/processing.php',
async:false, //fails without setting to false
...
});
});
...
$("#form-id").on('submit', function(event) {
$.ajax({
type: 'POST',
url: '/url/to/processing2ThatSetsSession.php',
async:false, //fails without setting to false
...
});
});
You have to concat the calls, to run one call after the other has ended.
I'll do it this way:
function ajaxPost(url, callback) {
$.ajax({
type: 'POST',
url: url
...
}).done(callback);
}
$("#form-id").on('submit', function(event) {
event.preventDefault(); // Always stop the event
// Do one ajax call and wait for the data
ajaxPost('/url/to/processing.php', function(data) {
// Do things with returned data and call the next ajax
ajaxPost('/url/to/processing.php', function(moredata) {
// Do something with moredata
// If everything is fine, re-post it but this time do not catch the event
$("#form-id").off("submit").submit();
});
});
});
You can add your own logic to show your error message in any callback and not continue with the next one.
With this I'll do an special method for multiple ajax form validation:
// This function will get an array of objects and
// do an ajax call and process the data, one after another
function multiAjax(calls, callback) {
var call = calls.shift();
if (call) {
var url = call.url;
post(url, function(data) {
var error = call.process(data);
if (error) {
callback(error);
} else {
multiAjax(calls, callback);
}
});
} else {
callback();
}
}
// This is the array of objects that multiAjax will process.
// You can add or remove elements to your likings, without modifying
// the submit event callback
var ajaxArray = [{
url: '/url/to/processing.php',
process: function(data) {
if (data.isWrong()) {
return "The data is wrong";
}
}
}, {
url: '/url/to/processing.php',
process: function(data) {
if (data != "OK") {
return "The data is not OK";
}
}
}];
// Now listen for the submit event
$("#form-id").on('submit', function(event) {
// Always stop the event
event.preventDefault();
// Do multiple ajax calls in one function call.
// Because the array is mutated inside multiAjax() (yeah, bad design but I've
// done this fast as an example), we slice() the array to get a new one
multiAjax(ajaxArray.slice(), function(error) {
if (error) {
// Show the error received
} else {
// submit the form the same way above
$("#form-id").off("submit").submit();
}
});
});
This is all untested code, but you get the point.
If one form submission is making two posts to the same PHP server, you should rethink the architecture instead of building complicated workarounds.
I would POST to a single PHP script that will do everything you need in the backend.
$.ajax({
type: 'POST',
url: '/url/to/all-processing.php',
... // send all the data needed by all processes
});
On the PHP side: all-processing.php
session_start();
require_once './process1.php';
require_once './process2.php';
Related
I have 4 tabs on my page:
MY Queue | Today | Followup | Upcoming
on page load i fire 4 ajax calls to get data from controller for all these tabs and once i get data i create list for each of the tabs.
But as ajax is asynchronous i get anomalies in my data, are there any better ways to achieve this.
i have 4 ajax calls similar to below call:
$.ajax({
url: '/opd_clinical_workflow/get_appointment_lists',
dataType: 'json',
data: {
current_date: current_date,
department_id: current_department,
doctor: current_doctor,
status: current_status,
source: "list",
q: $( "#search_appointment").val(),
},
success: function(res){
console.log(tab,res)
_this.updateMyQueueSummary(res,id,tab);
},
error: function(err){
console.log(err);
}
});
updateMyQueueSummary:
Puts data in respective tabs
createSummaryAppointment:
Creates html for me and is called in updatesummary
Chaining AJAX is your best option:
$.ajax({
url: "somewhere/1"
})
.always(function(reponse1) {
//Do something with the response
document.write("<p>1</p>");
//Then call next part
$.ajax({
url: "somewhere/2"
})
.always(function(reponse2) {
//Do something with the response
document.write("<p>2</p>");
//Then call next part
$.ajax({
url: "somewhere/3"
})
.always(function(reponse3) {
//Do something with the response
document.write("<p>3</p>");
//Then call next part
$.ajax({
url: "somewhere/4"
})
.always(function(reponse4) {
//Do something with the response
document.write("<p>4</p>");
//Now finalize it
document.write("<p>Final</p>");
})
})
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Notice i am using always instead of done because i don't have anywhere to actually get data from.
All the jQuery ajax methods return promise objects. This are kind of like an IOU note from a friend. You don't have the data now but it will be there at some point.
They are an extremely powerful tool if you want to avoid what is called "callback soup" where you nest ajax callbacks until the code is both unreadable and brittle.
var promise = $.ajax('/foo');
promise.pipe(function(data){
return $.ajax('/bar?foo=' + data['foo']);
});
promise.done(function(data){
// ajax call to '/bar?foo=' is complete
});
You can use jQuery.when to combine two promises. This is really useful when you want to fire two asynchronous ajax calls at once but you need the results of both to continue.
var calls = $.when( $.ajax('/foo'), $.ajax('/bar') );
calls.done(function(response_a, response_b){
// both ajax calls are complete.
});
I have a piece of code I want to run after all the ajax is completed.
The function I wish to run is:
function autoContinueCart(){
$('.nextSection a:visible').click();
}
This click event runs validating script and moves to next section. Heres the main ajax.
$('#SubmitLoginOpc').click(function () {
$.ajax({
type:'POST',
url:authenticationUrl,
async:false,
cache:false,
dataType:"json",
data:'SubmitLogin=true&ajax=true&email=' + encodeURIComponent($('#login_email').val()) + '&passwd=' + encodeURIComponent($('#login_passwd').val()) + '&token=' + static_token,
success:function (jsonData) {
if (jsonData.hasError) {
//error stuff
}
else {
// update token
static_token = jsonData.token;
$('#dlv_label, #new_label').removeClass('new-l').addClass('logged-l'); // change label on delivery address section
updateNewAccountToAddressBlock();
// RESET ERROR(S) MESSAGE(S)
$('#opc_account_errors').html('').hide();
$('#opc_account_errors_invoice').html('').hide();
//It doesnt work here
//autoContinueCart();
}
},
//doesnt work here
// complete:autoContinueCart
});
return false;
});
I have put this function call in the success part, which I thought would work since it is synchronous. I also put it as complete and in .done function after the ajax call and it still runs before all the inside code is complete. The function updateNewAccountToAddressBlock(); basically makes another jquery ajax request with this type async:true, and returns json that is then used in about 10 functions or sub functions in the success call. One of these uses this data to fill out all the fields of a form. My function I am trying to call at the end is supposed to validate the info that is being populated. But no matter what I try, the validation is failing because the autoContineCart is being run before the fields are being populated. I also tried to use a callback like updateNewAccountToAddressBlock(updateAddressSelection); and then checked callback function inside of that and it also didnt work. Anyone have an idea what I could be doing wrong?
Since your call is already asynchronous, is it possible to move the processing code out of the ajax callback function? This would ensure that all of the ajax portion is complete before moving on to the processing piece.
Example:
$('#SubmitLoginOpc').click(function () {
$.ajax({
type:'POST',
url:authenticationUrl,
async:false,
cache:false,
dataType:"json",
data:'SubmitLogin=true&ajax=true&email=' + encodeURIComponent($('#login_email').val()) + '&passwd=' + encodeURIComponent($('#login_passwd').val()) + '&token=' + static_token
},
success: function(jsonData) {
$('#SubmitLoginOpc').data("some_key",jsonData);
}
//doesnt work here
// complete:autoContinueCart
});
jsonData = $('#SubmitLoginOpc').data("some_key");
if (jsonData.hasError) {
//error stuff
}
else {
// update token
static_token = jsonData.token;
$('#dlv_label, #new_label').removeClass('new-l').addClass('logged-l'); // change label on delivery address section
updateNewAccountToAddressBlock();
// RESET ERROR(S) MESSAGE(S)
$('#opc_account_errors').html('').hide();
$('#opc_account_errors_invoice').html('').hide();
//It doesnt work here
//autoContinueCart();
return false;
}
});
As the poster above said, Perhaps you could move some of the other ajax functions to run from the same js file, or move some of the Php functions to be run at the same time as the first call.
Ideally you shouldn't have to do another ajax request, because the php/whatever already has the info it needs from the client side. You should be able to send that data to other php/whatever scripts.
If you do need to do another ajax call, perhaps having the user wait a mandatory second, before you run the ajax call.
for instance:
$ajax.done(
// code
if (success)
{
setTimeout('foo', 5000);
$('#spinner').show();
}
function foo()
{
$('#spinner').hide();
//second ajax request
}
Im trying to sort this around, im making an ajax requests of get type, and the page will be returning many responses, OK is one of those, is the page returns OK then I need to proceed for form submitting, else I need to halt the form submission, but Im unable to access the data out side of the function, it is available inside the fall back function alone, is it due to asynchronous mannerism?, can some one assist me to get out of this?
$.get('ajax.html',function(data){
//some code here
});
if(data == 'ok'){return true; } else {return false;}
$.get fires an asynchronous request, and the execution of you program continues. So in your case data is available only after the ajax call finishes and only inside your callback function. You need to do whatever you want to do with data inside your callback function.
You need to re-arrange you code to do something like:
$.get('ajax.html', function (data) {
if(data === 'OK') {
// do what you need to do in this case
}
});
// at this point there's no data
No, you are accessing data which is not a variable available.
How about assigning it to another variable?
var resp = "";
get('ajax.html', function (data) {
resp = data;
});
if(resp == "OK") {alert(1);}
alternatively you can go use asyn false
$.ajax({
url: url,
type: "GET",
async: false,
data: data,
success: function(ret) {
//alert(ret);
resp = data;
}
});
alert(resp);
Cheers!
I need to check for a condition and run an AJAX call before sending other AJAX calls on my web app.
I was thinking about putting this AJAX call in a beforeSend on ajaxSetup with async: false (to prevent my initial call from running before this one has completed).
Something like this:
//I set an event that fires:
$.ajax({
type: "GET",
url: my_url,
beforeSend: function() {
//do something, like show a spinner loader gif
}
});
//Somehwere in my app I also have:
$.ajaxSetup({
beforeSend: function() {
if(x===1){
$.ajax({
type: "GET",
url: my_url/fetch_something,
async:false
});
}
}
});
Will my beforeSend on the first AJAX call overrun the one in the ajaxSetup? Is there a way to approach this better?
Better idea of my app:
I have a lot of Ajax calls through the app, each call sends a security hash on the headers to validate the user, these hashes have a time limit as well (both hash and time limit are saved in localStorage)
What I want from ajax setup (and the condition in it) is to check for the time limit - if time_limit < current_time than run an ajax call to refresh the users hash.
This isn't an exercise for 1 or 2 calls, I literally have 20+ growing Ajax calls on my app that make use of the users hash and it's very impractical to make this check in every single one of them.
UPDATED:
Have one method on an interval that sets up the 'session'/local-storage
var refreshing = false;
var intervalID;
$(document).ready(function(e){
var delay = 1234;
intervalID = window.setInterval(setupInterval, delay);
});
function setupInterval(){
refreshing = true;
$.ajax(URL).done(function(r) { //do stuff
setupStorage(r);
refreshing = false;
});
}
function setupStorage(info){
//setup whatever here
}
OLD:
Could you use some logic in your ready function to gate what you need to do?
So basically call one ajax call -> if false, just schedule your latter methods, otherwise run the setup one and on completion schedule the latter method.
Some pseudo-code:
var refresh = false;
$(document).ready(function(e){
$.ajax(URL).done( function(r) {
if(r) {
routeOne();
} else {
latter();
}
});
});
function routeOne(){
$.ajax(URL).done(function(r) { //do stuff
latter();
});
}
function latter(){
//All other ajax calls
}
I'll put some more thought into this let me finish my coffee first...
EDIT:
Based on your updated description could it be possible for you to schedule a setInterval to run the checking method/hash update on the time interval that you need, and is the time interval on your server static or variable? Facebook does this with a heartbeat, I've used this type of logic with some 'locking' functionality in a web-app. If you schedule the interval properly it should not interrupt any other ajax calls.
Try overriding $.ajax to make a "pre-call" before passing in your given query options:
var oldAjax = $.ajax;
$.ajax = function() {
var args = arguments;
oldAjax({
type: "GET",
url: "/echo/html/",
success: function(result){
// do something here to check result
// if result is good, do the request:
return oldAjax.apply($, args);
// if its bad, handle the error
}
});
}
Here's a fiddle to demonstrate: http://jsfiddle.net/NF76U/
I suggest the use of .done() ( $.Deferred object)
function AjaxCall() {
return //code of your ajax without async:false
}
function anotherAjaxCall{
return //code of you ajax call
}
AjaxCall.done(anotherAjaxCall);
Avoid using async:false it's a deprecated practice and it stucks browsers
I have the following code which is included in a keypress function:
$.getJSON('dimensions.json', function(data) {
$.each(data, function(index) {
$('#div1').append(index);
});
});
I'm trying to first get the JSON string, save it in a variable and then run the each(). I want to basically separate the each() to be unlinked to the getJSON() function because I don't want it to fetch the json file for every keypress.
I've tried this, but it didn't work:
var JSONstr = $.getJSON('dimensions.json');
$.each(JSONstr, function(index) {
$('#div1').append(index);
});
In your first example, you do $.each in the callback. The callback is executed by some other callback after there result is received, while $.getJSON returns immediately without waiting for the result (since there is no blocking in JavaScript by design).
Therefore the code in your second example can never work: the $.each begins before any result is received from the web server, probably even before the request is sent. Whatever the return value of $.getJSON is, it can't, by the design of JavaScript, be the result of AJAX request.
UPD: Saw your comment, now I understand what you wanted to do. Here's a simple example of how to do this:
function ActualHandler(data) {
$.each(data, function(index) {
$('#div1').append(index);
});
}
function KeypressHandler() {
if (window.my_data) { // If we have the data saved, work with it
ActualHandler(window.my_data);
}
else { // Otherwise, send the request, wait for the answer, then do something
$.getJSON('dimensions.json', function(data) {
window.my_data = data; // Save the data
ActualHandler(data); // And *then* work on it
});
}
}
Here, the ActualHandler is not launched before the data is received, and once that happens, all subsequent clicks will be handled immediately.
The downside in this particular case is that if user clicks again while the first request is running, one more will be sent. But to fix that you would need to maintain some queue, which is kind of out of scope here.
You fell into the asynchronous trap. Your $.each() function doesn't wait for your $.getJSON() call to get the data. You can get around this by using the good 'ol $.ajax() function. Like this:
function processJSON(data) {
$.each(data, function(index) {
$('#div1').append(index);
});
}
$.ajax({
url: 'dimensions.json',
dataType: 'json',
async: false,
success: processJSON(data)
});