I need to execute a long (10-60 sec) PHP task from Ajax. Furthermore, the calling page needs to close the connection and go to a new page, with the PHP task finishing in the background.
At first I tried this scenario:
$.ajax({
url: '/ff.php',
type: 'POST'
}).success(function(response) {
console.log("... returned");
});
window.top.location.href = "http://yahoo.com";
But that never executes ff.php as I thought it should. Somehow it goes to the new page before calling the Ajax... or the connection is closed due to the new page, before it executes. This, despite the precautions I thought I needed in ff.php:
ignore_user_abort(true);
ob_end_clean();
header("Connection: close");
ignore_user_abort();
ob_start();
echo ('Text the user will see');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();
flush();
// Do processing here
session_write_close();
sleep(30);
require_once($_SERVER['DOCUMENT_ROOT'].'/ajax/lib/sendPush.php');
I've seen numerous posts that that top block of code is what's needed to have the PHP return control to the caller immediately but I can't get it to work in this scenario -- I suspect it's because I'm moving to a new page. That last require statement sends a push notification to my app on my iPhone, which woks just fine. I use this to know that my PHP script has been executed and it will part of my production environment when this works.
I have also tried moving the new page URL into the Ajax success block as follows, since I thought the PHP had all the code necessary to return immediately.
$.ajax({
url: '/ff.php',
type: 'POST'
}).success(function(response) {
console.log("... returned");
window.top.location.href = "http://yahoo.com";
});
But in that case, it takes the full 30 secs before going to the next page.
The desired results are: execute the PHP, receive the push right away, and the page goes to the next page quickly. But so far I either get the next page immediately, with NO execution of ff.php or, it does execute but the browser waits the full 30 secs to go to the next page.
UPDATE: As one commenter said below "basically if you redirect before the response then your ajax call will be cancelled." ... so my follow up question is this... is there no way to know when the Ajax has been received and started execution, so I can do the redirect at that time? I looked through the Ajax events and they all seemed to be related exclusively to when execution is complete. Does this imply there is no way to handle a long ajax call via jQuery? We have to use a queue?
Try something like this:
$.ajax({
url: '/ff.php',
type: 'POST'
}).success(function(response) {
console.log("... returned");
});
$('#status').text("I'm redirecting you to a new page...");
setTimeout(function(){window.top.location.href = "http://yahoo.com";},1000); //Put some delay between ajax and redirect
Redirect after success:
$.ajax({
url: '/ff.php',
type: 'POST',
success: function(response) {
console.log("Returned: " + response);
window.top.location.href = "http://yahoo.com";
}
});
Calling jQuery AJAX is async method, so either wait for success event, or use async: false.
And as quoted in this answer, first output content, than close the connection in PHP:
ignore_user_abort(true);
set_time_limit(0);
ob_start();
echo ('Text the user will see');
$size = ob_get_length();
header("Connection: close");
header("Content-Length: $size");
ob_end_flush();
ob_flush();
flush();
session_write_close();
// Do processing below:
Basically if you redirect before the response then your ajax call will be cancelled.
You should look for alternatives for example you can call the ajax request just to schedule the long running tasks.
Even if you find a way to hack this and to make it work, It would not be a good idea, you will never know the request was successful.
So just use cron task if you running linux, or Task Scheduler for Windows Server
According to documentation of ignore_user_abort you should pass true as parameter so i'll be run as long as you want.
edit: i didn't notice it's already there.
Once called PHP function through AJAX, should run until end processing regardles of user abort (or close AJAX connection).
Of course you need also set set_time_limit to extend execution time of script.
Related
I need to execute an AjaX call to a PHP script with a long execution time. My goal is to display a progress status of this execution.
The idea is to create an AjaX call to periodically ask the server about the status of the execution. The progress status is stored into $_SESSION['progress'], initially set to 0 and changed from script during execution.
Here's my code on client and server side.
Client-side
// invoke the script (ie. with button)
$('#start').click(function()
{
$.ajax
({
url: "long-script.php"
});
});
// check progress periodically
setInterval(progress, 100);
function progress()
{
$.ajax
({
dataType: "json",
url: "progress.php",
success: function(data)
{
console.log(data);
}
});
}
long-script.php
// just an example to emulate execution
sleep(1);
$_SESSION['progress']=30;
sleep(1);
$_SESSION['progress']=70;
sleep(1);
$_SESSION['progress']=100;
progress.php
header('Content-Type: application/json');
echo json_encode($_SESSION['progress']);
The problem is that console.log() in progress function outputs 0 before the script execution, stops outputing data during the execution, and finally outputs 100 when the script is terminated. What am I missing?
The problem is that the session is not written, until the script ends, or the session closes.
You need to remember that sessions, by default in php are stored as files on the system and is locked in run-time.
What you can do is change the long-script.php file a bit.
session_start();
sleep(1);
$_SESSION['progress']=30;
session_write_close();
sleep(1);
session_start();
$_SESSION['progress']=70;
session_write_close();
sleep(1);
session_start();
$_SESSION['progress']=100;
session_write_close();
The idea is to write to the session after every progress changes.
Then you will need to start the session again.
This may be a wrong way to do this, but you can always look up the session functions in php. Take a look at this session_write_cloe
I have php script which can take quite a lot of time (up to 3-5 minutes), so I would like to notify user how is it going.
I read this question and decided to use session for keeping information about work progress.
So, I have the following instructions in php:
public function longScript()
{
$generatingProgressSession = new Zend_Session_Namespace('generating_progress');
$generatingProgressSession->unsetAll();
....
$generatingProgressSession->total = $productsNumber;
...
$processedProducts = 0;
foreach($models as $model){
//Do some processing
$processedProducts++;
$generatingProgressSession->processed = $processedProducts;
}
}
And I have simple script for taking data from session (number of total and processed items) which return them in json format.
So, here is js code for calling long script:
$.ajax({
url: 'pathToLongScript',
data: {fileId: fileId, format: 'json'},
dataType: 'json',
success: function(data){
if(data.success){
if(typeof successCallback == "function")
successCallback(data);
}
}
});
//Start checking progress functionality
var checkingGenerationProgress = setInterval(function(){
$.ajax({
url: 'pathToCheckingStatusFunction',
data: {format: 'json'},
success: function(data){
console.log("Processed "+data.processed+" items of "+data.total);
if(data.processed == data.total){
clearInterval(checkingGenerationProgress);
}
}
});
}, 10000)
So, long scripted is called via ajax. Then after 10 seconds checking script is called one time, after 20 second - second time etc.
The problem is that none of requests to checking script is completed until main long script is complete. So, what does it mean? That long script consumes too many resources and server can not process any other request? Or I have some wrong ajax parameters?
See image:
-----------UPD
Here is a php function for checking status:
public function checkGenerationProgressAction()
{
$generatingProgressSession = new Zend_Session_Namespace('generating_progress');
$this->view->total = $generatingProgressSession->total;
$this->view->processed = $generatingProgressSession->processed;
}
I'm using ZF1 ActionContext helper here, so result of this function is json object {'total':'somevalue','processed':'another value'}
I'd
exec ('nohup php ...');
the file and send it to background. You can set points the long running script is inserting a single value in DB to show it's progress. Now you can go and check every ten or whatever seconds if a new value has been added and inform the user. Even might be possible to inform the user when he is on another page within your project, depending on your environment.
Yes, it's possible that the long scripts hogs the entire server and any other requests made in that time are waiting to get their turn. Also i would recommend you to not run the check script every 10 seconds no matter if the previous check has finished or not but instead let the check script trigger itself after it has been completed.
Taking for example your image with the requests pending, instead of having 3 checking request running at the same time you can chain them so that at any one time only one checking request is run.
You can do this by replacing your setInterval() function with a setTimeout() function and re-initialize the setTimeout() after the AJAX check request is completed
Most likely, the following calls are not completing due to session locking. When one thread has a session file open, no other PHP threads can open that same file, as it is read/write locked until the previous thread lets go of it.
Either that, or your Server OR Browser is limiting concurrent requests, and therefore waiting for this one to complete.
My solution would be to either fork or break the long-running script off somehow. Perhaps a call to exec to another script with the requisite parameters, or any way you think would work. Break the long-running script into a separate thread and return from the current one, notifying the user that the execution has begun.
The second part would be to log the progress of the script somewhere. A database, Memcache, or a file would work. Simply set a value in a pre-determined location that the follow-up calls can check on.
Not that "pre-determined" should not be the same for everyone. It should be a location that only the user's session and the worker know.
Can you paste the PHP of "pathToCheckingStatusFunction" here?
Also, I notice that the "pathToCheckingStatusFunction" ajax function doesn't have a dataType: "json". This could be causing a problem. Are you using the $_POST['format'] anywhere?
I also recommend chaining the checks into after the first check has completed. If you need help with that, I can post a solution.
Edit, add possible solution:
I'm not sure that using Zend_namespace is the right approach. I would recommend using session_start() and session_name(). Call the variables out of $_SESSION.
Example File 1:
session_name('test');
session_start();
$_SESSION['percent'] = 0;
...stuff...
$_SESSION['percent'] = 90;
Example File 2(get percent):
session_name('test');
session_start();
echo $_SESSION['percent'];
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 Have a PHP script which need to be run in background and with the help of
ignore_user_abort(true);
Script can be run even close the browser.
But I don't want to close browser every time,
$.ajax({
type: 'POST',
url: 'myphp.php',
data: values,
success: function(re) {alert("somthing");}
});
browser always wait for AJAX response even without mentioning of success:
Is there any way to stop browser waiting time, so that user can browse website normally without waiting for finishing of php script.
handle request with
jQuery XHR
var xmlHttpRequest = $.ajax( {
//....
});
xmlHttpRequest.abort();
At the beginning of your PHP code try to put this :
ob_start();
ob_end_flush();
That will send content to your ajax script and stop it.
I didn't try so it's just an idea ;)
You could try
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
though apparently whether it works depends on the exact PHP version. Or you could just terminate the AJAX call from the client side after a while (possibly after you start receiving content).
That said, the essence of AJAX calls is that "user can browse website normally without waiting" even while they are running, so I'm not sure there is a point to what you are trying to do.
i have a php web application, where i use an authentication method. I have a script logout.php in the same directory as the index file.
I want that the code in the logout.php be executed if the used mid session decides to exit or navigate away from the page.
ive tried using
function closeIt()
{
var exit = confirm("Are you sure you want to end this chat session ?");
if(exit==true){
$.ajax({
type: "GET",
url: "logout.php",
success: function(){ alert("Done");}
});
}
}
window.onbeforeunload = closeIt;
i get the confirm box,
but i am not getting success, am i doing somethign worng or do i need a new approach all together ?
The Ajax call is performed asynchronously, so the call is made and processing is passed back to the page immediately, which then closes before the ajax call completes.
You need to make a synchronous call to make this work.
A in AJAX stands for asynchronous. You may want to use synchronous XMLHttpRequest or return false at the end of closeIt() (that should prevent closing window) and in your success function, change onbeforeunload to null and close the window with window.close()