Some operations take too much time, which lead the ajax request to time out.
How do I finish responding to the request first, then continue that operation?
Some operations take too much time, which lead the ajax request to time out.
How do I finish responding to the request first, then continue that operation?
The ignore_user_abort
directive, and ignore_user_abort
function are probably what you are looking for : it should allow you to send the response to the browser, and, after that, still run some calculations on your server.
This article about it might interest you : How to Use ignore_user_abort() to Do Processing Out of Band ; quoting :
EDIT 2010-03-22 : removed the link (was pointing to http:// ! waynepan.com/2007/10/11/ ! how-to-use-ignore_user_abort-to-do-process-out-of-band/
-- remove the spaces and !
if you want to try ), after seeing the comment of @Joel.
Basically, when you use
ignore_user_abort(true)
in your php script, the script will continue running even if the user pressed the esc or stop on his browser. How do you use this?
One use would be to return content to the user and allow the connection to be closed while processing things that don’t require user interaction.The following example sends out
$response
to the user, closing the connection (making the browser’s spinner/loading bar stop), and then executesdo_function_that_takes_five_mins();
And the given example :
ignore_user_abort(true);
header("Connection: close");
header("Content-Length: " . mb_strlen($response));
echo $response;
flush();
do_function_that_takes_five_mins();
(There's more I didn't copy-paste)
Note that your PHP script still has to fit in the max_execution_time
and memory_limit
constraints -- which means you shouldn't use this for manipulations that take too much time.
This will also use one Apache process -- which means you should not have dozens of pages that do that at the same time.
Still, nice trick to enhance use experience, I suppose ;-)
Spawn a background process and return background process id so user can check on it later via some secret URL.
Sort of depends on what you're trying to accomplish.
In my case, I needed to do a bunch of server-side processing, with only a minimal amount of data being sent back to the browser - summary info really.
I was trying to create a message sender - sends out an email to over 250 people, but possibly many more (depends on how many have registered with the system).
the PHP mail handler is quick, but for large numbers, not quick enough, so it was bound to time out. To get around that, i needed to delay the timeout on the server/PHP side, and keep the browser hanging on till all data was summarized and displayed.
My solution - a teaser.
essentially, i gave the user a message stating some initial stats (attempting to send this many emails), created 2 DIV boxes (one for current status info, the second for final summary info), displayed the page footer, started the processing, and when finished, updated the summary info. it goes as follows:
Start by collecting your data you're going to process, and get some summary info. In my case, i pulled the list of email addresses, validated them and counted them.
Then display the "attempting" info:
echo "Message contents:";
echo "<blockquote>$msgsubject<p>$msgbody</blockquote><p> </p>";
echo "Attempting <strong>" . $num_rows . "</strong> email addresses.";
Now create some DIVs for status/final info:
<div id=content name=content>
<div id=progress name=progress style='border: black 1px solid; width: ".$boxwidth."px; height: 20px;'></div>
<br>
</div>
where $boxwidth is the width you'd like your progress bar to be (explained later)
Notice that they are essentially empty - we'll fill them later.
Finally, fill out the rest of the page by displaying the footer of the page (in my case I just "included" the appropriate file).
Now, all that is still hanging out in the page buffer, either on the server (if PHP is buffering) or the browser (because it hasn't been told we're done yet), so let's cause it to get pushed and/or displayed using the "ignore" and "flush" from above:
ignore_user_abort(true);
flush();
Now that the browser is starting to display stuff, we need to give the user something to see, so here's the tease - we'll create a status bar that we'll display in the inner DIV, and a final message for the outer DIV.
So, as you loop through your data, periodically (I'll leave "how often" up to you to figure out), output the following:
set_time_limit (3);
...
(process your data here)
...
<script>document.getElementById('progress').innerHTML += "<img src='images/progress_red.gif' width: $width height=20 border=0>"</script>
flush();
This will essentially stack up the little 4x20 progress_red images next to each other, making it appear that a red bar is moving across the screen. In my case, i did this 100 times, based on a percentage of what number I was currently processing out of the total number to process. The "set_time_limit" will add 3 seconds to the existing limit, so that you know your script won't run into the PHP time limit wall. Adjust the 3 seconds for your application as needed.
even though the page is technically "complete", the javascript code will update the DIV html and our progress bar will "move".
when all done, i then report on how many items were actually processed and add that to the outer DIV html and the page is complete:
<script>document.getElementById('content').innerHTML += "Message was sent to $sentcnt people."</script>
The browser is happy because it's had all it's qualifications met (end of page syntactially), the user sees somethign happening right up to the end, and the server
I tend not to use Javascript if I can help it, but in the case, there was no fancy CSS stuff being done through the Javascript, so it's pretty straightforward and easy to see whats going on and low overhead on the browser site. And it functions pretty smoothly.
I don't claim this is perfect, but for a simple, low overhead solution, it works for me without having to create cronjobs, extra queuing mechanisms or secondary processes.