Мне было любопытно, и я решил попробовать предложение xhr.abort()
из моего комментария.
Ниже приведен лишь базовый пример выполнения долго выполняющегося запроса AJAX PHP, при котором клиент не ожидает ответа от сервера или, скорее, досрочно прекращает выполнение сценария XHR и PHP.
Этот подход можно адаптировать для запуска локального сценария, который также отправляет запрос cURL или SOAP внешнему хосту.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Send It</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<form method="post" action="https://second.domain.com/recievingScript.php">
<p><input type="text" name="name"/></p>
<p>
<button type="submit">Send</button>
</p>
</form>
<script type="text/javascript">
const writeLog = function(msg) {
let date = new Date();
window.console.log(date.toISOString() + ' ' + msg);
};
jQuery(function($) {
let f = $('form');
let xhrOptions = {
url: f.attr('action'),
type: f.attr('method'),
data: {},
cache: false,
xhr: function() {
let xhr = $.ajaxSettings.xhr();
if ('undefined' === typeof xhr.upload.onload || null === xhr.upload.onload) {
//override the upload.onload event, only if it has not already been
xhr.upload.onload = function() {
//onload is triggered immediately after the POST headers are sent to the recipient
writeLog('Upload Completed - Ending Request');
xhr.abort();
};
}
return xhr;
},
};
f.on('submit', function(e) {
e.preventDefault();
let formData = f.serialize();
writeLog(formData);
$.ajax($.extend(true, xhrOptions, {
data: formData
})).done(function(responseText) {
//this is never triggered since the request is aborted
writeLog('Success');
writeLog(responseText);
}).fail(function(jqxhr) {
writeLog('Request ' + (jqxhr.readyState !== 4 ? 'Aborted' : 'Failed'));
});
return false;
});
});
</script>
</body>
</html>
Ответ:
send.html:18 2019-01-15T21:19:11.445Z name=Hello%20Dagroa
send.html:18 2019-01-15T21:19:11.558Z Upload Completed - Ending Request
send.html:18 2019-01-15T21:19:11.558Z Request Aborted
\date_default_timezone_set('UTC');
\ignore_user_abort(true);
\header('Access-Control-Allow-Origin: https://first.domain.com');
if (!empty($_POST)) {
$dateStart = new \DateTimeImmutable();
//create a placeholder file
if ($file = \tempnam(__DIR__, 'tmp_')) {
$i = 1;
//PHP >= 7 required for random_int - PHP < 7 use mt_rand() instead
$rand = \random_int(5, 30);
//add the number of seconds to create a stop date
$dateStop = $dateStart->add(new \DateInterval(\sprintf('PT' . $rand . 'S')));
while (new \DateTime() < $dateStop) {
//loop until the stop date is reached
$i++;
}
$dateEnd = new \DateTime();
$dateDiff = $dateEnd->diff($dateStart);
//write result to the temporary file
\file_put_contents($file, \json_encode([
'file' => \basename($file),
'scriptFile' => \basename($_SERVER['SCRIPT_FILENAME']),
'iterations' => $i,
'start' => $dateStart->format(\DATE_RFC3339_EXTENDED),
'end' => $dateEnd->format(\DATE_RFC3339_EXTENDED),
'stop' => $dateStop->format(\DATE_RFC3339_EXTENDED),
'elapsed' => $dateDiff->format('%i minutes, %s seconds, %f microseconds'),
'slept' => $rand,
'post' => $_POST,
], \JSON_PRETTY_PRINT));
}
}
Ответ:
{
"file": "tmp_hjLk4y",
"scriptFile": "recievingScript.php",
"iterations": 9653192,
"start": "2019-01-15T21:19:12.171+00:00",
"end": "2019-01-15T21:19:36.171+00:00",
"stop": "2019-01-15T21:19:36.171+00:00",
"elapsed": "0 minutes, 24 seconds, 3 microseconds",
"slept": 24,
"post": {
"name": "Hello Dagroa"
}
}
Примечания: Я использую php-fpm 7.2 в качестве прокси-сервера fcgi, используя Apache 2.4, который
не должно иметь значения, кроме как при вызове функции random_int
и DATE_RFC3339_EXTENDED
.
Дополнительно
const
и let
в JavaScript используется спецификация ECMAScript 6, которая поддерживается всеми текущими основными версиями браузера.
Буферизацию вывода PHP следует отключить, в противном случае может потребоваться принудительная очистка, используя flush()
и / или ob_end_flush()
. Некоторые другие условия конфигурации вашего веб-сервера также могут влиять на буферизацию вывода, например, кодирование gzip
.
jQuery выдаст метод запроса OPTIONS
непосредственно перед методом запроса POST
. Убедитесь, что $_POST
не пусто, или проверьте $_SERVER['REQUEST_METHOD']
для нужного типа, чтобы предотвратить двойное выполнение.