Pregunta Barra de progreso con PHP y Ajax


Estoy trabajando en la barra de progreso, que actualiza el progreso utilizando las solicitudes ajax y las variables de la sesión. Cuando mi programa realiza una operación que consume mucho tiempo, como enviar muchos correos electrónicos, etc., simplemente establece la variable de sesión adecuada (que contiene el valor de progreso). Esta operación se inicia mediante la función post () en el código a continuación.

Mientras tanto, la segunda función ask () se realiza en bucle cada 500 ms. Debería devolver el progreso actual en tiempo real. Y aquí está el problema: cada solicitud enviada por ask () está esperando que la solicitud enviada por la función post () haya finalizado. Lo gracioso es que si configuro una URL como google.com en lugar de url / to / progress, funciona bien, excepto que no es lo que quiero :). Lo que significa que el problema está en el lado del servidor.

No estoy seguro de si es importante pero utilizo Yii Framework.

Todo el código a continuación es solo cero (pero funciona) y su único propósito es mostrar lo que quise decir.

Gracias por adelantado.

Disculpa mi pobre ingles :)

Ver parte:

<script type="text/javascript">
function ask() {
  var d = new Date();
  var time = d.getTime();
  $.ajax({

    type: 'get',
    url: '/url/to/progress' + '?time=' + time,
    success: function(data) {
      $("#progress").html(data);
    }
  })
}

function post() {
  var d = new Date();
  var time = d.getTime();

  $.ajax({
      type: 'post',
      url: '/url/to/post' + '?time=' + time,
      data: {"some": "data"},
      success: function(data) {alert(data)}
    });
}

$("#test").click(
  function() {
    post();
    var progress = setInterval("ask();", 500);
  }
);
</script>

Parte del controlador:

public function actionPost($time) {
  sleep(5); // time consuming operation
  echo $time . ' : ' . microtime();
  exit;
}

public function actionProgress($time) {
  echo $time . ' : ' . microtime();
  exit;
}

7
2018-02-16 13:19


origen


Respuestas:


Creo que su problema aquí está relacionado con la sesión.

Cuando un script tiene una sesión abierta, tiene un bloqueo en el archivo de sesión. Esto significa que cualquier solicitud subsiguiente que use la misma ID de sesión se pondrá en cola hasta que la primera secuencia de comandos haya liberado su bloqueo en el archivo de sesión. Puedes forzar esto con session_write_close() - pero esto no te ayudará mucho aquí, ya que estás tratando de compartir la información de progreso con el archivo de sesión post el script necesitaría mantener los datos de la sesión abiertos y escribibles.

Tendrá que encontrar otra forma de compartir datos entre post y progress scripts - si post tiene los datos de la sesión abiertos durante su ejecución, progress nunca podrá acceder a los datos de la sesión hasta después post ha terminado de ejecutar. Tal vez podrías usar la ID de la sesión para crear un archivo temporal que post tiene acceso de escritura a, en el que pone los datos del indicador de progreso. los progress puede verificar el archivo y devolver esos datos. Hay muchas opciones para IPC (comunicación entre procesos): esta no es particularmente bonita, pero tiene la ventaja de la máxima portabilidad.

Como nota al margen - por favor no pase cadenas para setInterval(), pasa funciones Entonces tu línea debería leer:

var progress = setInterval(ask, 500);

Pero - sería mejor usar setTimeout() en el success/error controladores de la ask() función ajax Esto es porque al usar setInterval(), se iniciará una nueva solicitud independientemente del estado de la anterior. Sería más eficiente esperar hasta que la solicitud anterior haya terminado antes de iniciar la siguiente. Así que haría algo más como esto:

<script type="text/javascript">

  // We'll set this to true when the initail POST request is complete, so we
  // can easily know when to stop polling the server for progress updates
  var postComplete = false;

  var ask = function() {

    var time = new Date().getTime();

    $.ajax({

      type: 'get',
      url: '/url/to/progress' + '?time=' + time,

      success: function(data) {
        $("#progress").html(data);
        if (!postComplete)
          setTimeout(ask, 500);
        }
      },
      error: function() {
        // We need an error handler as well, to ensure another attempt gets scheduled
        if (!postComplete)
          setTimeout(ask, 500);
        }
      }

    });

  }

  $("#test").click(function() {

    // Since you only ever call post() once, you don't need a seperate function.
    // You can just put all the post() code here.

    var time = new Date().getTime();

    $.ajax({

      type: 'post',
      url: '/url/to/post' + '?time=' + time,
      data: {
        "some": "data"
      },

      success: function(data) {
        postComplete = true;
        alert(data);
      }
      error: function() {
        postComplete = true;
      }

    });

    if (!postComplete)
      setTimeout(ask, 500);
    }

  });

</script>

... aunque esto todavía no soluciona el problema de la sesión.


9
2018-02-16 14:03



@DaveRandom arriba señala correctamente que usted es víctima de un bloqueo de almacenamiento de sesión.

La solución es bastante simple. Quieres hacer el script que procesa post() liberar el bloqueo en los datos de la sesión para que la secuencia de comandos que maneja ask()puede acceder a esta información de sesión. Puedes hacer eso con session_write_close.

La letra pequeña aquí es que después de llamar session_write_close no tendrá acceso a las variables de sesión, por lo que necesita estructurar el script para post en consecuencia:

  1. Lee todos los datos que necesitarás de $_SESSION y guarda una copia de eso
  2. Llamada session_write_close para liberar el bloqueo de la sesión.
  3. Continúa con tu larga operación. Si necesita datos de sesión, sáquelos de su propia copia y no $_SESSION directamente.

Alternativamente, puede alternar el bloqueo de la sesión varias veces durante la vigencia del script:

session_start();
$_SESSION['name'] = 'Jon';

// Quick operation that requires session data
echo 'Hello '.$_SESSION['name'];

//  Release session lock
session_write_close();

// Long operation that does not require session data.
sleep(10);

// Need access to session again
session_start();
echo 'Hello again '.$_SESSION['name'];

Esta disposición hace que, mientras el script duerme, otros scripts puedan acceder a los datos de la sesión sin problemas.


6
2018-02-16 14:07



Preguntas populares