75 votes

Pourquoi le navigateur bloquerait-il plusieurs appels AJAX simultanés à la même action ASP.NET MVC ?

Il y a quelques jours, j'ai posé cette question :

Pourquoi $.getJSON() bloque-t-il le navigateur ?

Je lance six requêtes ajax asynchrones jQuery sur la même action de contrôleur, presque toutes en même temps. Chaque requête met 10 secondes à revenir.

En déboguant et en enregistrant les requêtes de la méthode d'action, je remarque que les requêtes sont sérialisées et ne s'exécutent jamais en parallèle, c'est-à-dire que je vois une ligne de temps dans mes journaux log4net comme ceci :

2010-12-13 13:25:06,633 \[11164\] INFO   - Got:1156
2010-12-13 13:25:16,634 \[11164\] INFO   - Returning:1156
2010-12-13 13:25:16,770 \[7124\] INFO   - Got:1426
2010-12-13 13:25:26,772 \[7124\] INFO   - Returning:1426
2010-12-13 13:25:26,925 \[11164\] INFO   - Got:1912
2010-12-13 13:25:36,926 \[11164\] INFO   - Returning:1912
2010-12-13 13:25:37,096 \[9812\] INFO   - Got:1913
2010-12-13 13:25:47,098 \[9812\] INFO   - Returning:1913
2010-12-13 13:25:47,283 \[7124\] INFO   - Got:2002
2010-12-13 13:25:57,285 \[7124\] INFO   - Returning:2002
2010-12-13 13:25:57,424 \[11164\] INFO   - Got:1308
2010-12-13 13:26:07,425 \[11164\] INFO   - Returning:1308

En regardant la chronologie du réseau dans FireFox, je vois ceci :

alt text

L'échantillon de journal ci-dessus et la chronologie du réseau Firefox concernent tous deux le même ensemble de demandes.

Les demandes de la même action à partir de la même page sont-elles sérialisées ? Je suis au courant de l'accès en série à la Session dans la même session, mais aucune donnée de session n'est touchée.

J'ai réduit le code côté client à une seule requête (la plus longue) mais cela bloque toujours le navigateur, c'est-à-dire que le navigateur ne répond à un clic sur un lien que lorsque la requête ajax est terminée.

Ce que j'observe également ici (dans les outils de développement de Chrome), c'est que lorsque l'on clique sur un lien alors qu'une requête ajax longue durée est en cours d'exécution, un message d'erreur est généré. Failed to load resource qui suggère que le navigateur a tué (ou tente de tuer et attend ?) la requête ajax :

alt text

Cependant, le navigateur met toujours un certain temps à rediriger vers la nouvelle page.

Les requêtes ajax sont-elles réellement asynchrones ou s'agit-il d'un tour de passe-passe parce que le javascript est en fait monofilaire ?

Mes demandes sont-elles trop longues pour que cela fonctionne ?

Le problème se produit également dans Firefox et IE.

J'ai aussi changé le script pour utiliser $.ajax directement et explicitement fixé async: true .

Je l'exécute sur IIS7.5, les versions Windows 2008R2 et Windows 7 font la même chose.

Les builds Debug et Release se comportent également de la même manière.

0 votes

Avez-vous testé cela dans firefox/IE. Peut-être est-ce un problème de chrome ?

0 votes

@aseem - c'est la même chose dans F/Fox et IE

0 votes

Peut-être que votre script change le paramètre asynchrone quelque part ailleurs.

91voto

Kev Points 60744

La réponse me sautait aux yeux.

Aperçu de l'état de la session ASP.NET :

L'accès à l'état de la session ASP.NET est exclusif par session, ce qui signifie que si deux utilisateurs différents font des demandes simultanées, l'accès à chaque session distincte est accordé simultanément. Cependant, si deux demandes concurrentes sont faites pour la même session (en utilisant la même valeur SessionID), la première demande obtient un accès exclusif aux informations de la session. La seconde demande ne s'exécute qu'une fois la première terminée.

Malheureusement, j'avais survolé ce paragraphe il y a quelques semaines, sans vraiment prendre la mesure de l'impact des phrases en gras. Je l'avais lu simplement comme "l'accès à l'état de la session est sérialisé" et non "toutes les demandes, qu'elles touchent ou non l'état de la session, sont sérialisées" si les demandes proviennent de la même session.

Heureusement, il existe une solution dans ASP.NET MVC3 et il est possible de créer des contrôleurs sans session. Scott Guthrie en parle ici :

Annonce d'ASP.NET MVC 3 (Release Candidate 2)

J'ai installé MVC3 RC2 et mis à jour le projet. En décorant le contrôleur en question avec [SessionState(SessionStateBehavior.Disabled)] résout le problème.

Et bien sûr, typiquement, je viens de trouver ça sur Stack Overflow il y a quelques minutes :

Le contrôleur asynchrone bloque les demandes dans ASP.NET MVC grâce à jQuery

0 votes

Merci pour cela. J'avais exactement le même problème.

0 votes

Merci de m'aider à résoudre un problème difficile ! C'est moi ou c'est un comportement par défaut stupide et flagrant ?

6 votes

Notez que l'utilisation de [SessionState(SessionStateBehavior.Disabled)] peut ouvrir une brèche de sécurité dans votre application, par exemple l'accès à des données privées dans de multiples requêtes simultanées, avec cette option vous n'avez aucun moyen de savoir si un utilisateur est connecté, ni si une session est active, etc ... Si vous avez besoin d'accéder à la session vous pouvez utiliser [SessionState(SessionStateBehavior.ReadOnly)] Vous avez donc toujours accès à votre session, bien que vous ne puissiez pas la modifier, mais cela peut être utile dans de nombreux cas.

9voto

Darin Dimitrov Points 528142

J'ai essayé de reproduire ce phénomène mais je n'y suis pas parvenu. Voici mon test :

private static readonly Random _random = new Random();

public ActionResult Ajax()
{
    var startTime = DateTime.Now;
    Thread.Sleep(_random.Next(5000, 10000));
    return Json(new { 
        startTime = startTime.ToString("HH:mm:ss fff"),
        endTime = DateTime.Now.ToString("HH:mm:ss fff") 
    }, JsonRequestBehavior.AllowGet);
}

Et l'appel :

<script type="text/javascript" src="/scripts/jquery-1.4.1.js"></script>
<script type="text/javascript">
    $(function () {
        for (var i = 0; i < 6; i++) {
            $.getJSON('/home/ajax', function (result) {
                $('#result').append($('<div/>').html(
                    result.startTime + ' | ' + result.endTime
                ));
            });
        }
    });
</script>

<div id="result"></div>

Et les résultats :

13:37:00 603 | 13:37:05 969
13:37:00 603 | 13:37:06 640
13:37:00 571 | 13:37:07 591
13:37:00 603 | 13:37:08 730
13:37:00 603 | 13:37:10 025
13:37:00 603 | 13:37:10 166

Et la console FireBug :

alt text

Comme vous pouvez le voir, l'action AJAX est exécutée en parallèle.


UPDATE :

Il semble que, lors de mes premiers tests, les requêtes soient effectivement mises en file d'attente dans FireFox 3.6.12 et Chrome 8.0.552.215 lorsque l'on utilise $.getJSON() . Il fonctionne bien dans IE8. Mes tests ont été effectués avec un projet ASP.NET MVC 2, VS2010, serveur web Cassini, Windows 7 x64 bit.

Maintenant, si je remplace $.getJSON() avec $.get() il fonctionne bien sous tous les navigateurs. Cela m'amène à penser qu'il y a quelque chose avec ce $.getJSON() ce qui pourrait provoquer la mise en file d'attente des demandes. Peut-être que quelqu'un de plus familier avec les mécanismes internes du framework jQuery pourrait apporter plus de lumière sur ce sujet.


UPDATE 2 :

Essayez de régler cache: false :

$.ajax({
    url: '/home/ajax', 
    cache: false,
    success: function (result) {
        $('#result').append($('<div/>').html(
            result.startTime + ' | ' + result.endTime
        ));
    }
});

0 votes

@Kev, oui c'est un peu étrange. Pourriez-vous essayer de remplacer $.getJSON() por $.get() ? J'ai testé cela avec Cassini. Je n'ai pas installé IIS et ne peux pas vérifier.

0 votes

@darin - J'ai essayé $.getJSON() et est allé directement à $.ajax() et les résultats sont les mêmes.

0 votes

L'exécution de cet échantillon dans IE8 fonctionne bien pour moi. Cependant, dans FF et Chrome, les demandes ne sont pas envoyées en parallèle. J'utilise cassini et VS2010.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X