I have a form that is validated then submitted with the following handler :
submitHandler:function (form) {
$.ajax({
url: 'long_process.php',
type: 'POST',
data: $(form).serialize(),
success: function (result) {
// ... Redirect ...
}
});
//start polling
(function poll() {
setTimeout(function () {
$.ajax({
url: "get_progress.php",
success: function (data) {
console.log(data);
//Setup the next poll recursively
poll();
},
});
}, 3000);
})();
}
long_process.php takes about 30s to finish and in the meantime I'd like to track the progress via get_progress.php which echo the percentage of processing done.
When launching this script I get in the console (edited):
1 POST long_process.php
2 GET get_process.php (3 seconds later)...
...stuck here until long_process.php finishes THEN
3 GET get_process.php (3 seconds later)...
4 GET get_process.php (3 seconds later)...
...
but none of the get_progress.php return any values until the long_process.php is finished.
How can I achieve multiple simultaneous ajax request ? Ultimately this will be used to display a progress bar.
If you are using sessions in your PHP then the one call will block the other, because PHP won't allow two requests to use the same session space at the same time. Your two requests are probably firing, but you are not getting a request to the second until the server finishes with the first.
To solve this:
Option 1: Don't use sessions in the first script that you are calling or, if you must, then unlock the session using session_write_close(). After calling this, of course, you can't write any session variables.
Option 2: If reading and writing session variables is essential then don't use session variables in the second AJAX call and don't declare a session start. Have your first script put it's status somewhere else for you to read (in a DB, a file on /tmp) and then have the second script read the status from there.
Hope that helps!
Related
I want to run 2 AJAX calls simultaneously. I tried the below code but it waits for ajax ({export.php}) to finish and then ajax({display.php}) is run.
I want to run these 2 AJAX calls at the same time. For example, while export.php is running it should also run display.php at same time. How can I do this?
When I click on a button it calls export.php to convert the database table to a CSV file. I also want to show the progress bar i.e. how many records have finished, 1%, 2%... 100%. That percentage value is written in the display.php file so to make a progress bar I need to run the 2 AJAX calls simultaneously.
$('.tocsv').on('click', function() {
var display = setInterval(function() {
$.ajax({
url: 'display.php',
method: 'get',
success: function(data) {
console.log(data);
}
});
}, 500);
$.ajax({
url: 'export.php',
method: 'post',
success: function() {
clearInterval(display);
}
});
});
Edit
The problem was in display.php file i had written session_start(). I just deleted that line and changed ajax code to this
url: 'display.php?file=<?=session_id()?>',
success: function (data) {
$('#res').html(data);
}
});
but why it doesn't work when i write session_start() ?
In AJAX the first alphabet A is for "Asynchronous". So calling asyn way is not the issue here. The problem is with making ajax request inside the setInterval. I am very sure that you server is not responding in the time delay you have given i.e. 500ms and you are flooding your server with multiple request every half second. By the time one request is completed you have made at least 4 or 5 request to your server which is the root cause of you issue. Remove setInterval and if you want you call to be made after 0.5 sec use setTimeout.
Once my page is loaded, I perform an Ajax call to a php script, which updates my server. However this script can sometimes take over a minute to complete, and while the script is running, I am unable to perform other Ajax calls, which I need to handle - i.e the first Ajax call should not interrupt the other Ajax calls. Any idea how to do this?
First Ajax call:
$(document).ready(function () {
$.ajax({
url: "checkForUpdatesByCoach.php",
success: function(arg){
if(arg == "200"){
$('body').prepend("<div style='text-align:center;margin-bottom:-12px;' onClick='location.reload()' class='alert alert-success'>Dine hold er blevet opdateret.Tryk for at opdatere!</div>").fadeIn("slow");
}
}
});
});
Second Ajax call (a user triggered call):
$.ajax({
type: "POST",
data: {data:dataAjax},
url: "updateSwimmer1.php",
success: function(arg){
//updating UI
}
});
adeno's comment above is correct.
"in PHP only one script at a time can operate on the same session, so
as to not overwrite session data etc. So when doing two ajax calls to
PHP scripts within the same session, the second has to wait for the
first to finish"
to help speed things up you can write to and end a session early(session_write_close()) to release the session-lock and allow another script using the session to continue.
note: you can still read from your $_SESSION variable after calling session_write_close but you may no longer write to it.
you can find a good example of this here: PHP Session Locks – How to Prevent Blocking Requests
example provided from the link above:
<?php
// start the session
session_start();
// I can read/write to session
$_SESSION['latestRequestTime'] = time();
// close the session
session_write_close();
// now do my long-running code.
// still able to read from session, but not write
$twitterId = $_SESSION['twitterId'];
// dang Twitter can be slow, good thing my other Ajax calls
// aren't waiting for this to complete
$twitterFeed = fetchTwitterFeed($twitterId);
echo json_encode($twitterFeed);
?>
I've made a simple PHP jQuery Chat Application with Short Polling (AJAX Refresh). Like, every 2 - 3 seconds it asks for new messages. But, I read that Long Polling is a better approach for Chat applications. So, I went through some Long Polling scripts.
I made like this:
Javascript:
$("#submit").click(function(){
$.ajax({
url: 'chat-handler.php',
dataType: 'json',
data: {action : 'read', message : 'message'}
});
});
var getNewMessage = function() {
$.ajax({
url: 'chat-handler.php',
dataType: 'json',
data: {action : 'read', message : 'message'},
function(data){
alert(data);
}
});
getNewMessage();
}
$(document).ready(getNewMessage);
PHP
<?php
$time = time();
while ((time() - $time) < 25) {
$data = $db->getNewMessage ();
if (!empty ($data)) {
echo json_encode ($data);
break;
}
usleep(1000000); // 1 Second
}
?>
The problem is, once getNewMessage() starts, it executes unless it gets some response (from chat-handler.php). It executes recursively. But if someone wants to send a message in between, then actually that function ($("#submit").click()) never executes as getNewMessage() is still executing. So is there any workaround?
I strongly recommend that you read up on two things: the idea behind long polling, and jQuery callbacks. I'll quickly go into both, but only in as much detail as this box allows me to.
Long polling
The idea behind long polling is to have the webserver artificially "slow down" when returning the request so that it waits until an event has come up, and then immediately gives the information, and closes the connection. This means that your server will be sitting idle for a while (well, not idle, but you know what I mean), until it finally gets the info that a message went through, sends that back to the client, and proceeds to the next one.
On the JS client side, the effect is that the Ajax callback (this is the important bit) is delayed.
jQuery .ajax()
$.ajax() returns immediately. This is not good. You have two choices to remedy this:
bind your recursion call in the success and error callback functions (this is important. the error function might very well come up due to a timeout)
(see below):
Use This:
var x = $.ajax({blah});
$.when(x).done(function(a) { recursiveCallHere(); });
Both amount to the same thing in the end. You're triggering your recursion on callback and not on initiation.
P.S: what's wrong with sleep(1)?
In long polling new request should be initiated when you have received the data from the previous one. Otherwise you will have infinite recursion in browser freezing.
var getNewMessage = function() {
$.ajax({
url: 'chat-handler.php',
dataType: 'json',
data: {action : 'read', message : 'message'},
success: function(data) {
alert(data);
getNewMessage(); // <-- should be here
}
});
}
I want to run some AJAX calls at the same page from the same client.
Ajax calls start correctly but the server queued the requests and execute jsut one per time.
I've also check the start request time and the returned message time.
Studying the second one there's a difference between the requests which is multiple than the before request.
Help me please!
$("document").ready(function() {
$(".user-id").each(function() {
var id = $(this).html();
getData(id);
});
});
function getData(id) {
$.ajax({
url: 'loadOperatorDiagram.php',
type: 'GET',
data: {id: id},
async: true,
cache: false,
success: function(resp) {
$("#boxes").append(resp);
draw(id); // A javascript function which draw into a canvas
}
});
}
loadOperatorDiagram.php get some queries and its execution time is about 5 seconds. The first one ajax request response after 5 seconds, the second one after 10 and so on. But everyone starts asyncronusly and correctly with a difference of few milliseconds
If you are using sessions in php (sounds like it, otherwise you could do at least 2 simultaneous requests...), you should close it as soon as possible in your php script as php will block the session.
Just use session_write_close(); as soon as you have what you need from the session.
I am working on progress bar which updates progress using ajax requests and session variables. When my program performs time consuming operation such as sending many emails etc. it just set proper session variable (which contains progress value). This operation is started by function post() in code below.
In the meantime, the second function ask() is performed in loop every 500ms. It should return current progress in real time. And here is the problem: every request sent by ask() are waiting for request sent by post() function is finished. The funny part is that if I set some URL like google.com instead of url/to/progress it works just fine except that it is not what I want :). Which means that problem is on the server side.
Not sure if it's important but I use Yii Framework.
All the code below is only scratch (but working) and its only purpose is to show what I meant.
Thanks in advance.
Sorry for my poor english :)
View part:
<script type="text/javascript">
function ask() {
var d = new Date();
var time = d.getTime();
$.ajax({
type: 'get',
url: '/url/to/progress' + '?time=' + time,
success: function(data) {
$("#progress").html(data);
}
})
}
function post() {
var d = new Date();
var time = d.getTime();
$.ajax({
type: 'post',
url: '/url/to/post' + '?time=' + time,
data: {"some": "data"},
success: function(data) {alert(data)}
});
}
$("#test").click(
function() {
post();
var progress = setInterval("ask();", 500);
}
);
</script>
Controller part:
public function actionPost($time) {
sleep(5); // time consuming operation
echo $time . ' : ' . microtime();
exit;
}
public function actionProgress($time) {
echo $time . ' : ' . microtime();
exit;
}
I think your problem here is session related.
When a script has an open session, it has a lock on the session file. This means that any subsequent requests which use the same session ID will be queued until the first script has released it's lock on the session file. You can force this with session_write_close() - but this won't really help you here, as you are trying to share the progress info with the session file so post script would need to keep the session data open and writable.
You will need to come up with another way of sharing data between the post and progress scripts - if post has the session data open throughout it's execution, progress will never be able to access the session data until after post has finished executing. Maybe you could use the session ID to create a temporary file which post has write access to, in which you put the progress indicator data. The progress can check the file and return that data. There are many options for IPC (inter-process communication) - this is not a particularly beautiful one but it does have the advantage of maximum portability.
As a side note - please don't pass strings to setInterval(), pass functions. So your line should actually read:
var progress = setInterval(ask, 500);
But - it would be better to use setTimeout() in the success/error handlers of the ask() ajax function. This is because by using setInterval(), a new request will be initiated regardless of the state of the previous one. It would be more efficient to wait until the previous request has finished before initiating the next one. So I would do something more like this:
<script type="text/javascript">
// We'll set this to true when the initail POST request is complete, so we
// can easily know when to stop polling the server for progress updates
var postComplete = false;
var ask = function() {
var time = new Date().getTime();
$.ajax({
type: 'get',
url: '/url/to/progress' + '?time=' + time,
success: function(data) {
$("#progress").html(data);
if (!postComplete)
setTimeout(ask, 500);
}
},
error: function() {
// We need an error handler as well, to ensure another attempt gets scheduled
if (!postComplete)
setTimeout(ask, 500);
}
}
});
}
$("#test").click(function() {
// Since you only ever call post() once, you don't need a seperate function.
// You can just put all the post() code here.
var time = new Date().getTime();
$.ajax({
type: 'post',
url: '/url/to/post' + '?time=' + time,
data: {
"some": "data"
},
success: function(data) {
postComplete = true;
alert(data);
}
error: function() {
postComplete = true;
}
});
if (!postComplete)
setTimeout(ask, 500);
}
});
</script>
...although this still doesn't fix the session problem.
#DaveRandom above correctly points out that you are a victim of a session storage lock.
The workaround is quite simple. You want to make the script that processes post() release the lock on the session data so that the script that handles ask() can access this session data. You can do that with session_write_close.
The fine print here is that after calling session_write_close you will not have access to session variables, so you need to structure the script for post accordingly:
Read all data you will need from $_SESSION and save a copy of it.
Call session_write_close to release the session lock.
Continue your lengthy operation. If you need session data, get it out of your own copy and not $_SESSION directly.
Alternatively, you can toggle the lock on the session multiple times during the script's lifetime:
session_start();
$_SESSION['name'] = 'Jon';
// Quick operation that requires session data
echo 'Hello '.$_SESSION['name'];
// Release session lock
session_write_close();
// Long operation that does not require session data.
sleep(10);
// Need access to session again
session_start();
echo 'Hello again '.$_SESSION['name'];
This arrangement makes it so that while the script it sleeping, other scripts can access the session data without problems.