Как продолжить процесс после ответа на запрос ajax в PHP?

Некоторые операции занимают слишком много времени, что приводит к аннулированию запроса ajax.

Как мне сначала ответить на запрос, а затем продолжить эту операцию?

Директива ignore_user_abort и ignore_user_abort – это, вероятно, то, что вы ищете: она должна позволить вам отправлять ответ браузеру и после этого выполнять некоторые вычисления на вашем сервере.

Эта статья об этом может вас заинтересовать: Как использовать ignore_user_abort () для обработки вне диапазона ; цитирование:
EDIT 2010-03-22: удалила ссылку (указывала на http:// ! waynepan.com/2007/10/11/ ! how-to-use-ignore_user_abort-to-do-process-out-of-band/ – удалите пробелы и ! если вы хотите попробовать) , увидев комментарий @Joel.

В основном, когда вы используете ignore_user_abort(true) в вашем php-скрипте, скрипт будет продолжать работать, даже если пользователь нажал esc или остановился в своем браузере. Как вы это используете?
Одно из них – вернуть контент пользователю и разрешить закрытие соединения при обработке вещей, которые не требуют взаимодействия с пользователем.

Следующий пример отправляет пользователю $response , закрывая соединение (делая do_function_that_takes_five_mins(); spinner / do_function_that_takes_five_mins(); bar stop), а затем выполняет do_function_that_takes_five_mins();

И приведенный пример:

 ignore_user_abort(true); header("Connection: close"); header("Content-Length: " . mb_strlen($response)); echo $response; flush(); do_function_that_takes_five_mins(); 

(Больше я не копировал-вставлял)

Обратите внимание, что ваш PHP-скрипт по-прежнему должен соответствовать ограничениям max_execution_time и memory_limit что означает, что вы не должны использовать это для манипуляций, занимающих слишком много времени.

Это также будет использовать один процесс Apache – это означает, что вы не должны иметь десятки страниц, которые делают это в одно и то же время.

Тем не менее, хороший трюк, чтобы улучшить опыт использования, я полагаю 😉

Создайте фоновый процесс и верните идентификатор фонового процесса, чтобы пользователь мог проверить его позже через какой-то секретный URL.

Отчасти зависит от того, что вы пытаетесь выполнить.

В моем случае мне нужно было сделать кучу серверной обработки, и только минимальное количество данных было отправлено обратно в браузер – сводная информация действительно.

Я пытался создать отправителя сообщения – отправляет электронное письмо более чем 250 людям, но, возможно, еще много (зависит от количества зарегистрированных в системе).

обработчик почты PHP быстр, но для больших чисел, не достаточно быстрый, поэтому он был вынужден тайм-аут. Чтобы обойти это, мне нужно было отложить тайм-аут на стороне сервера / PHP и держать браузер нависшим, пока все данные не будут суммированы и не отображены.

Мое решение – тизер.

по существу, я дал пользователю сообщение с указанием исходной статистики (попытки отправить это много писем), создал 2 коробки DIV (один для текущей информации о состоянии, второй для окончательной итоговой информации), отобразил нижний колонтитул страницы, начал обработку и когда закончите, обновите итоговую информацию. это выглядит следующим образом:

Начните с сбора данных, которые вы собираетесь обрабатывать, и получите некоторую итоговую информацию. В моем случае я вытащил список адресов электронной почты, подтвердил их и подсчитал.

Затем отобразите «попытку»:

 echo "Message contents:"; echo "<blockquote>$msgsubject<p>$msgbody</blockquote><p>&nbsp;</p>"; echo "Attempting <strong>" . $num_rows . "</strong> email addresses."; 

Теперь создайте некоторые DIV для информации о статусе / финале:

 <div id=content name=content> <div id=progress name=progress style='border: black 1px solid; width: ".$boxwidth."px; height: 20px;'></div> <br> </div> 

где $ boxwidth – это ширина, в которой вам нужен индикатор выполнения (поясняется ниже)

Обратите внимание, что они по существу пусты – мы их заполним позже.

Наконец, заполните оставшуюся часть страницы, показывая нижний колонтитул страницы (в моем случае я просто «включил» соответствующий файл).

Теперь все, что все еще болтается в буфере страниц, либо на сервере (если PHP буферизуется), либо в браузере (потому что нам еще не сказали, что мы закончили), так что давайте заставим его нажать и / или отображается с помощью «ignore» и «flush» сверху:

 ignore_user_abort(true); flush(); 

Теперь, когда браузер начинает отображать материал, нам нужно дать пользователю что-то посмотреть, поэтому вот дразнить – мы создадим строку состояния, которую мы будем отображать во внутреннем DIV, и окончательное сообщение для внешнего DIV ,

Поэтому, периодически повторяя свои данные, я оставляю «как часто» до вас, чтобы выяснить, выводит следующее:

 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(); 

Это будет по существу складывать маленькие изображения 4×20 progress_red рядом друг с другом, заставляя казаться, что красная полоса перемещается по экрану. В моем случае я сделал это 100 раз, исходя из процента того числа, которое я обрабатывал в настоящее время из общего числа для обработки. «Set_time_limit» добавит 3 секунды к существующему лимиту, так что вы знаете, что ваш скрипт не будет работать на стене временного ограничения PHP. Отрегулируйте 3 секунды для вашего приложения по мере необходимости.

даже если страница технически «завершена», код javascript обновит DIV html, и наш индикатор прогресса «переместится».

когда все сделано, я затем сообщаю о том, сколько элементов было фактически обработано, и добавьте это во внешний HTML-файл DIV, и страница завершена:

 <script>document.getElementById('content').innerHTML += "Message was sent to $sentcnt people."</script> 

Браузер счастлив, потому что у него есть все, что соответствует квалификации (конец страницы синтаксически), пользователь видит, что что-то происходит до самого конца, а сервер

Я, как правило, не использую Javascript, если я могу ему помочь, но в этом случае не было никаких фантазийных вещей CSS, которые делались через Javascript, поэтому довольно просто и легко увидеть, что происходит, и низкие накладные расходы на сайте браузера. И он работает довольно гладко.

Я не утверждаю, что это идеально, но для простого, низкого накладных решений он работает для меня, не создавая cronjobs, дополнительные механизмы очередей или вторичные процессы.

Вам нужен вызов flush() чтобы немедленно отправить ответ в браузер.