Почему несколько одновременных вызовов AJAX к одному и тому же действию ASP.NET MVC заставляют браузер блокировать?

Несколько дней назад я задал этот вопрос:

Почему $.getJSON() блокирует браузер?

Я запускаю шесть jQuery async ajax-запросов при одном и том же действии контроллера почти все сразу. Каждый запрос занимает 10 секунд, чтобы вернуться.

Отлаживая и регистрируя запросы к методу действий, я замечаю, что запросы сериализованы и никогда не запускаются параллельно. т.е. я вижу временную шкалу в журналах log4net следующим образом:

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

Глядя на временную шкалу сети в FireFox, я вижу следующее:

alt text

Оба примера журнала и временная шкала сети Firefox предназначены для одного и того же набора запросов.

Выполняются ли запросы к одному и тому же действию с одной страницы? Я знаю о сериализованном доступе к объекту Session в том же сеансе, но данные сеанса не затрагиваются.

Я разделил код на стороне клиента на один запрос (самый длинный из них), но это все еще блокирует браузер, т.е. только когда запрос ajax завершается, браузер отвечает на любую ссылку, нажав.

То, что я также наблюдаю здесь (в инструментах разработчика Chrome), заключается в том, что после нажатия ссылки, когда выполняется длительный выполняемый запрос ajax, он сообщает об ошибке Failed to load resource, которая указывает на то, что браузер убил (или пытается убить и ожидания?) запрос ajax:

alt text

Однако браузеру еще требуется возраст для перенаправления на новую страницу.

Действительно ли аякс-запросы действительно асинхронны или это ругательство, потому что javascript на самом деле однопоточный?

Неужели мои запросы слишком долго заставляют работать?

Проблема возникает и в Firefox, и в IE.

Я также изменил script на использование $.ajax напрямую и явно установил async: true.

Я запускаю это на IIS7.5, оба варианта Windows 2008R2 и Windows 7 делают то же самое.

Отладки и выпуски также ведут себя одинаково.

Ответ 1

Ответ смотрел мне в лицо.

Обзор состояния сеанса ASP.NET:

Доступ к состоянию сеанса ASP.NET является исключительным для каждого сеанса, а это означает, что, если два разных пользователя выполняют параллельные запросы, доступ к каждому отдельному сеансу предоставляется одновременно. Тем не менее, , если два одновременных запроса сделаны для одного и того же сеанса (с использованием того же значения SessionID), первый запрос получает эксклюзивный доступ к информации о сеансе. Второй запрос выполняется только после завершения первого запроса.

Раздражающе, я пару недель назад снял абзац, чтобы не в полной мере повлиять на смелые предложения. Я читал, что просто, когда "доступ к состоянию сеанса сериализуется", а не "все запросы, независимо от того, касаетесь состояния сеанса или нет, сериализуются", если запросы поступают из одного сеанса.

К счастью, в ASP.NET MVC3 есть работа, и возможно создание контроллеров без сеанса. Скотт Гатри рассказывает об этом здесь:

Объявление ASP.NET MVC 3 (Release Candidate 2)

Я установил MVC3 RC2 и обновил проект. Оформление данного контроллера с помощью [SessionState(SessionStateBehavior.Disabled)] решает проблему.

И, конечно, обычно я только что нашел это в Qaru несколько минут назад:

Асинхронный контроллер блокирует запросы в ASP.NET MVC через jQuery

Ответ 2

Я пытался воспроизвести это, но не смог. Здесь мой тест:

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

И вызов:

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

И результаты:

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

И консоль FireBug:

alt text

Как вы видите, действие AJAX попадает параллельно.


UPDATE:

Похоже, что в моих первоначальных тестах запросы действительно поставлены в очередь в FireFox 3.6.12 и Chrome 8.0.552.215 при использовании $.getJSON(). Он отлично работает в IE8. Мои тесты выполнялись с помощью проекта ASP.NET MVC 2, VS2010, веб-сервера Cassini, Windows 7 x64 бит.

Теперь, если я заменил $.getJSON() на $.get(), он отлично работает во всех браузерах. Это заставляет меня думать, что есть что-то с этим $.getJSON(), которое может вызвать запросы в очередь. Может быть, кто-то, более знакомый с внутренностями структуры jQuery, сможет пролить свет на этот вопрос. Забастовкa >


ОБНОВЛЕНИЕ 2:

Попробуйте установить cache: false:

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