2011-08-01 3 views
12

J'ai une longue interrogation sur ma page. Le script côté serveur est défini sur le délai d'expiration après 20 secondes. Ainsi, lorsque l'interrogation longue est "inactive" et que l'utilisateur appuie sur un autre bouton, l'envoi de cette nouvelle requête est retardé jusqu'à l'expiration du script précédent.Plusieurs demandes AJAX se retardent mutuellement

Je ne vois rien de mal avec le code sur le côté jQuery. Pourquoi l'événement onclick est-il retardé?

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'; 
} 

Le «problème de sommeil» pourrait-il être le problème?

XHR Screenshot

+0

est le problème est là aussi localhost ?? –

+1

Est-ce que le code PHP pour l'appel AJAX utilise des sessions? –

Répondre

23

Si vous utilisez des sessions dans les fonctions de gestion AJAX, vous courrez dans un problème où le disque les données de session sont verrouillées par la première requête, de sorte que chaque requête suivante finit par attendre que les données de la session soient disponibles avant qu'elles ne se fassent.En effet, cela bloque les appels asynchrones, vous obtenez des réponses linéaires aux requêtes dans l'ordre chronologique - synchrone. (here's a reference article)

Une solution consiste à utiliser session_write_close (docs) pour fermer la session dès que vous n'en avez plus besoin. Cela permet aux autres requêtes suivantes de se poursuivre car les données de session seront "déverrouillées".

Ceci, cependant, peut également prêter à confusion. Si vous appelez session_write_close juste avant de renvoyer une réponse, alors vous ne vous ferez aucune faveur parce que la session aurait été déverrouillée dès que la réponse a été envoyée. Ainsi, il doit être appelé le plus tôt possible. Si vous utilisez une architecture de style post-back pour la requête AJAX, ce n'est pas si grave, mais si vous avez un framework plus grand et que votre gestionnaire de requêtes n'en est qu'une partie, vous devrez explorer une solution de plus haut niveau. à l'utilisation de session non bloquante afin que vos sous-composants ne ferment pas une session attendue par le framework est toujours ouverte.

Une route doit aller avec la session de base de données. Il y a des avantages et des inconvénients à cette solution qui dépassent le cadre de cette réponse - vérifiez Google pour une discussion exhaustive. Une autre route consiste à utiliser une fonction qui ouvre une session, ajoute une variable, puis la ferme. Vous risquez des conditions de concurrence avec cette solution, mais voici un aperçu:

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

Je n'ai pas exactement utilisé votre solution proposée, mais l'indice avec 'session_write_close()' était parfait! Mes demandes utilisaient des sessions, et les désactiver fonctionnait parfaitement bien. –

+1

'session_write_close()' a résolu mon problème, merci! –

1

Cela semble conforme à la règle de 2 demande - les navigateurs ne permettent que deux connexions simultanées sur le même hôte à tout moment donné. Cela étant dit, vous devriez être bien avec un long sondage (recevoir) et envoyer un canal. Commencez-vous le long sondage après le chargement de la page en utilisant $ (function() {...? Êtes-vous sûr que la demande est retardée sur le client, et non dans le navigateur? Que voyez-vous dans firebug?

+0

Oui, j'en suis sûr, car la requête n'apparaît même pas dans firebug pendant quelques secondes. Mais quand l'interrogation se termine et commence depuis le début, la requête apparaît, comme dans la capture d'écran ci-dessus. –

+0

J'ai testé l'AJAX seul, sans l'interrogation, et il a fallu environ 800ms pour terminer. Lorsque l'interrogation est en cours, cela prend toujours plus de 2 secondes. –

+0

Je suis d'accord ici. Bien que les nouveaux navigateurs permettent plus (FF permet 6 et IE 8 permet 8 je suppose). – Mrchief

1

One chose que vous pouvez faire, vous pouvez interrompre le scrutin en cours d'exécution et exécutez votre demande d'abord, puis recommencer le scrutin.

//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

On dirait que c'est traiter le symptôme plutôt que la cause –