2011-08-01 14 views
12

Tengo una solicitud de votación larga en mi página. La secuencia de comandos en el lado del servidor se establece en tiempo de espera después de 20 segundos.Varias solicitudes de AJAX se retrasan entre sí

Por lo tanto, cuando el sondeo largo está "inactivo", y el usuario presiona otro botón, el envío de esa nueva solicitud se retrasa hasta que el script anterior expira.

No puedo ver nada mal con el código en el lado jQuery. ¿Por qué se retrasa el evento onclick?

function poll() 
{ 
$.ajax({ 
    url: "/xhr/poll/1", 
    data: { 
     user_id: app.user.id 
    }, 
    type: "POST", 
    dataType: "JSON", 
    success: pollComplete, 
    error: function(response) { 
     console.log(response); 
    } 
}); 
} 

function pollComplete() 
{ 
    poll(); 
} 

function joinRoom(user_id) 
{ 
$.ajax({ 
    url: "/xhr/room/join", 
    dataType: "JSON", 
    type: "POST", 
    data: { 
     user_id: app.user.id, 
     room_id: room.id 
    } 
}); 
} 

<button id="join" onclick="javascript:joinRoom(2);">Join</button> 

############ PHP Controller on /xhr/poll 

$time = time(); 
while ((time() - $time) < 20) 
{ 
    $updates = $db->getNewStuff(); 

    foreach ($updates->getResult() as $update) 
     $response[] = $update->getResponse(); 

    if (!empty($response)) 
     return $response; 
    else 
     usleep(1 * 1000000); 

    return 'no-updates'; 
} 

¿Podría el "usleep" ser el problema?

XHR Screenshot

+0

¿El problema está en el localhost también? –

+1

¿El código PHP de cualquiera de las llamadas AJAX utiliza sesiones? –

Respuesta

23

Si utiliza sesiones en las funciones de manejo de AJAX, que se ejecutará en un problema por el que el disco la primera solicitud bloquea los datos de sesión, por lo que cada solicitud subsiguiente termina esperando a que los datos de la sesión estén disponibles antes de continuar. En efecto, esto hace que las llamadas asincrónicas se bloqueen entre sí, usted termina con respuestas lineales a las solicitudes en orden cronológico - sincrónico. (here's a reference article)

Una solución es usar session_write_close (docs) para cerrar la sesión tan pronto como ya no la necesite. Esto permite que procedan otras solicitudes posteriores porque los datos de la sesión se "desbloquearán".

Esto, sin embargo, puede ser confuso también. Si llama al session_write_close justo antes de devolver una respuesta, no se hará ningún favor porque la sesión se desbloqueó tan pronto como se envió la respuesta. Por lo tanto, debe llamarse tan pronto como sea posible. Si está utilizando una arquitectura de estilo posterior a la posterior para la solicitud de AJAX, esto no es tan malo, pero si tiene un marco más grande y su controlador de solicitud es solo una parte de él, tendrá que explorar una solución más de alto nivel al uso de sesiones sin bloqueo para que sus subcomponentes no cierren una sesión que el marco espera que todavía esté abierta.

Una ruta es para ir con la sesión de la base de datos. Existen ventajas y desventajas para esta solución que están más allá del alcance de esta respuesta: revise Google para una discusión exhaustiva. Otra ruta es usar una función que abre una sesión, agrega una variable y luego la cierra. Usted arriesga las condiciones de carrera con esta solución, pero aquí hay un esquema aproximado:

function get_session_var($key, $default=null) { 
    if (strlen($key) < 1) 
     return null; 
    if (!isset($_SESSION) || !is_array($_SESSION)) { 
     session_start(); 
     session_write_close(); 
    } 
    if (array_key_exists($key, $_SESSION)) 
     return $_SESSION[$key]; 
    return $default; 
} 
function set_session_var($key, $value=null) { 
    if (strlen($key) < 1) 
     return false; 
    if ($value === null && array_key_exists($key, $_SESSION)) { 
     session_start(); 
     unset($_SESSION[$key]); 
    } elseif ($value != null) { 
     session_start(); 
     $_SESSION[$key] = $value; 
    } else { 
     return false; 
    } 
    session_write_close(); 
    return true; 
} 
+1

No usé exactamente la solución propuesta, pero la pista con 'session_write_close()' era ¡Perfecto! Mis solicitudes usaban sesiones, y deshabilitarlas funcionaba perfectamente bien. –

+1

'session_write_close()' resolvió mi problema, gracias! –

1

Esto parece consistente con la regla 2 solicitud - navegadores sólo permiten dos conexiones simultáneas con el mismo host en cualquier momento dado. Habiendo dicho eso, deberías estar bien con una encuesta larga (recibir) y enviar un canal. ¿Está comenzando la encuesta larga después de cargar la página usando $ (función() {...? ¿Está seguro de que la solicitud se está retrasando en el cliente y no en el navegador? ¿Qué está viendo en Firebug?

+0

Sí, estoy seguro, porque la solicitud ni siquiera aparece en Firebug por unos segundos. Pero cuando el sondeo finaliza y comienza desde el principio, aparece la solicitud, como en la captura de pantalla anterior. –

+0

Probé el AJAX solo, sin el sondeo, y tardó alrededor de 800ms en completarse. Cuando el sondeo se está ejecutando, siempre lleva más de 2 segundos. –

+0

Estoy de acuerdo aquí. Aunque los navegadores más nuevos permiten más (FF permite 6 e IE 8 permite 8, supongo). – Mrchief

1

Uno que puede hacer, puede abortar la encuesta de correr y ejecutar la petición de primera y de nuevo comenzar la encuesta.

//make sure pollJqXhr.abort is not undefined 
var pollJqXhr={abort:$.noop}; 

function poll() 
{ 
    //assign actual jqXhr object 
    pollJqXhr=jQuery.ajax({youroptions}); 
} 

function pollComplete() 
{ 
    poll(); 
} 


function joinRoom(user_id) 
{ 
    //pause polling 
    pollJqXhr.abort(); 

    jquery.ajax({ 
      /*options here*/ 
      success:function() 
      { 
       /*Your codes*/ 

       //restart poll 
       poll() 
      } 
    }); 
} 
+1

Parece que esto es tratar el síntoma más que la causa –

Cuestiones relacionadas