javascript - ¿Existe la posibilidad de recibir varias respuestas AJAX del servidor PHP simultáneamente?
Quiero hacer una barra de progreso en mi sitio web, que rastree la ejecución de un script PHP.
El script de PHP realiza una serie de conexiones con la API de Google y almacena los datos que recibe en la base de datos. A veces, el proceso puede tardar un minuto.
El script PHP se encuentra en el archivo ajax/integrations-ajax.php y se lanza mediante una solicitud AJAX GET enviada, si en el sitio web se hace clic en el botón #link. A continuación se muestra el código jQuery para la solicitud:
$('#link').on('click', function () {
var interval = setInterval(trackStatus, 1000);
$.getJSON('ajax/integrations-ajax.php', {action: 'link'}).done(function (json) {
if (json.result == true) {
showMessage('The account is successfully linked.', 'success');
} else {
showMessage('There is an error happened.', 'danger');
}
})
});
Este botón #link, también establece un intervalo que ejecuta la función trackStatus cada segundo:
function trackStatus() {
$.getJSON('ajax/status-ajax.php', {
action: 'status'
}).done(function (json) {
console.log(json.status);
});
}
Como puedes ver, la función trackStatus envía solicitudes AJAX GET al archivo ajax/status-ajax.php y debería mostrar el estado en la consola del navegador cada segundo.
Para implementar la capacidad de seguimiento en el servidor, hice el script PHP en el archivo ajax/integrations-ajax.php para almacenar el estado en la base de datos. Su código se puede ver a continuación:
<?php
if(!is_ajax_request()) { exit; }
$action = isset($_GET['action']) ? (string) $_GET['action'] : '';
if ($action == 'link') {
set_status_in_database(0);
// some execution code;
set_status_in_database(1);
// some execution code;
set_status_in_database(2);
// some execution code;
set_status_in_database(3);
// some execution code;
echo json_encode(['result' => true ]);
}
Y se creó otro archivo PHP axax/status-ajax.php que puede recuperar el estado desde la base de datos:
<?php
if(!is_ajax_request()) { exit; }
$action = isset($_GET['action']) ? (string) $_GET['action'] : '';
if ($action == 'status') {
$return['result'] = get_status_from_database();
echo json_encode($return);
}
Pero las solicitudes parecen no funcionar simultáneamente. No puedo recibir respuestas de la función trackStatus hasta que no se reciba la respuesta del script ajax/integrations-ajax.php sobre su finalización.
Hice un registro de perfiles en el navegador, que muestra que:
Entonces, ¿hay posibilidad de ejecutar solicitudes simultáneamente? ¿O para implementar la capacidad de seguimiento necesito replantear todo el enfoque?
¡Gracias de antemano por la ayuda!
Actualización ¡Gracias a todos por sus consejos! Y especialmente a @Keith, porque su solución es la más fácil y funciona. He puesto la función session_write_close() al principio del script y todo funciona:
<?php
if(!is_ajax_request()) { exit; }
$action = isset($_GET['action']) ? (string) $_GET['action'] : '';
if ($action == 'link') {
session_write_close();
set_status_in_database(0);
// some execution code;
set_status_in_database(1);
// some execution code;
set_status_in_database(2);
// some execution code;
set_status_in_database(3);
// some execution code;
echo json_encode(['result' => true ]);
}
Aquí puedes ver un registro de perfilado desde un navegador:
Respuesta #1
Aunque PHP puede manejar solicitudes concurrentes sin problemas, un área que sí se serializa es la sesión. Básicamente, PHP durante una solicitud colocará un bloqueo exclusivo en la SESSION, para ese usuario. Es decir: mientras este bloqueo esté activado, otras solicitudes del mismo usuario tendrán que esperar. Normalmente esto no es un problema, pero si tienes solicitudes de larga duración, bloquea otras solicitudes, como solicitudes AJAX, etc.
Por defecto, PHP escribirá los datos de la sesión al final de la solicitud. Pero si estás seguro de que ya no necesitas escribir ningún dato de sesión, llamar a session_write_close liberará el bloqueo mucho antes.
Más información aquí -> http://php.net/manual/en/function/session-write-close.php
Respuesta #2
Recomendaría intentar con EventSource. Aquí hay un ejemplo.
PHP
<?php
header('Content-Type: text/event-stream');
// recommended to prevent caching of event data.
header('Cache-Control: no-cache');
function send_message($id, $message, $progress) {
$d = array('message' => $message , 'progress' => $progress);
echo "id: $id" . PHP_EOL;
echo "data: " . json_encode($d) . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
}
for($i=0; $i<4; $i++){
set_status_in_database($i);
// some execution code;
send_message($i, "set status in database " . $i + 1 . " of 3' , $i*4);
sleep(1);
}
send_message('CLOSE', 'Process complete');
?>
JavaScript
var es;
function startTask() {
es = new eventSource('ajax/status-ajax.php');
es.addEventListener('message', function(e) {
var result = JSON.parse(e.data);
console.log(result.message);
if(e.lastEventId == 'CLOSE') {
console.log('Received CLOSE closing');
es.close();
showMessage('The account is successfully linked.', 'success');
} else {
$('.progress').css("width", result.progress + '%');
}
});
es.addEventListener('error', function(e) {
console.log('Error occurred', e);
es.close();
});
}
function stopTask() {
es.close();
console.log('Interrupted');
}
$('#link').on('click', function(e) {
e.preventDefault();
startTask($(this));
});
Referencia :
- Mostrar informe de progreso para scripts PHP de larga duración
Espero que eso te sea útil.

