为什么对同一个 ASP.NET MVC 操作的多个同时 AJAX 调用会导致浏览器阻塞?

IT技术 javascript jquery asp.net-mvc ajax asp.net-mvc-2
2021-01-21 22:21:45

前几天我问了这个问题:

为什么 $.getJSON() 会阻止浏览器?

我几乎同时在同一个控制器操作上发出六个 jQuery 异步 ajax 请求。每个请求需要 10 秒才能返回。

通过调试和记录对 action 方法的请求,我注意到请求是序列化的,永远不会并行运行。即我在我的 log4net 日志中看到一个时间线,如下所示:

2010-12-13 13:25:06,633 [11164] 信息 - 得到:1156
2010-12-13 13:25:16,634 [11164] 信息 - 返回:1156
2010-12-13 13:25:16,770 [7124] 信息 - 得到:1426
2010-12-13 13:25:26,772 [7124] 信息 - 返回:1426
2010-12-13 13:25:26,925 [11164] 信息 - 得到:1912
2010-12-13 13:25:36,926 [11164] 信息 - 返回:1912
2010-12-13 13:25:37,096 [9812] 信息 - 得到:1913
2010-12-13 13:25:47,098 [9812] 信息 - 返回:1913
2010-12-13 13:25:47,283 [7124] 信息 - 得到:2002
2010-12-13 13:25:57,285 [7124] 信息 - 返回:2002
2010-12-13 13:25:57,424 [11164] 信息 - 得到:1308
2010-12-13 13:26:07,425 [11164] 信息 - 返回:1308

查看 FireFox 中的网络时间线,我看到:

替代文字

上面的日志示例和 Firefox 网络时间线都针对同一组请求。

来自同一页面的对同一操作的请求是否被序列化?我知道Session在同一个会话中对象的序列化访问,但没有触及任何会话数据。

我将客户端代码剥离为单个请求(运行时间最长的请求),但这仍然会阻止浏览器,即只有当 ajax 请求完成时,浏览器才会响应任何链接点击。

我在这里(在 Chrome 的开发人员工具中)还观察到的是,当一个长时间运行的 ajax 请求正在执行时点击一个链接,它会Failed to load resource立即报告一个错误,这表明浏览器已经杀死(或试图杀死并等待?)ajax要求:

替代文字

然而,浏览器仍然需要一段时间才能重定向到新页面。

ajax 请求是否真的是异步的,还是因为 javascript 实际上是单线程的,所以这是一种花招?

我的请求是否需要太长时间才能起作用?

Firefox 和 IE 中也会出现此问题。

我还将脚本更改为$.ajax直接使用并显式设置async: true

我在 IIS7.5 上运行它,Windows 2008R2 和 Windows 7 版本都做同样的事情。

调试和发布版本的行为也相同。

2个回答

答案是盯着我的脸。

ASP.NET 会话状态概述

对 ASP.NET 会话状态的访问是按会话独占的,这意味着如果两个不同的用户并发请求,则同时授予对每个单独会话的访问权限。但是,如果为同一个会话发出两个并发请求(通过使用相同的 SessionID 值),第一个请求将获得对会话信息的独占访问权。第二个请求仅在第一个请求完成后执行。

令人讨厌的是,几周前我略读了这一段,并没有真正考虑到大胆句子的全部影响。如果请求来自同一个会话,我只是将其理解为“对会话状态的访问被序列化”,而不是“所有请求,无论您是否触摸会话状态,都被序列化”

幸运的是,在 ASP.NET MVC3 中有一个变通方法,它可以创建无会话控制器。Scott Guthrie 在这里谈到了这些:

宣布 ASP.NET MVC 3(发布候选 2)

我安装了 MVC3 RC2 并升级了项目。装饰有问题的控制器可以[SessionState(SessionStateBehavior.Disabled)]解决问题。

当然,通常我几分钟前才在 Stack Overflow 中找到了这个:

异步控制器通过 jQuery 阻止 ASP.NET MVC 中的请求

@GregoireD。- 感谢您提供有关SessionStateBehavior.ReadOnly. 我有一种感觉,我看着它,它仍然会导致对您的Session. 该应用程序使用表单身份验证和 cookie/db 查找组合来锁定对该控制器的访问。从控制器本身返回的数据并不保密,但我们确实采取了措施防止人们出于无所事事的好奇而摆弄。
2021-03-19 22:21:45
谢谢你。我遇到了同样的问题。
2021-03-25 22:21:45
SessionStateBehaviour.ReadOnly 似乎在我的情况下有效,并且仍然允许访问会话。
2021-03-29 22:21:45
请注意,使用[SessionState(SessionStateBehavior.Disabled)]可能会破坏您的应用程序的安全性,例如在多个同时请求中访问私人数据,使用此选项您无法知道用户是否已登录,或者会话是否处于活动状态等......如果您需要访问您可以使用的会话[SessionState(SessionStateBehavior.ReadOnly)],因此您仍然可以访问您的会话,尽管您无法编辑它,但在许多情况下仍然可能有所帮助。
2021-03-31 22:21:45
感谢您帮助解决一个棘手的错误!是我一个人还是这种明显愚蠢的默认行为?
2021-04-08 22:21:45

我试图重现这一点,但无法重现。这是我的测试:

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 控制台:

替代文字

如您所见,AJAX 操作是并行执行的。


更新:

似乎在我的初始测试中,使用$.getJSON(). 它在 IE8 中运行良好。我的测试是使用 ASP.NET MVC 2 项目、VS2010、Cassini Web 服务器、Windows 7 x64 位执行的。

现在,如果我$.getJSON()$.get()替换它在所有浏览器下都可以正常工作。这让我相信$.getJSON()这可能会导致请求排队。也许更熟悉 jQuery 框架内部结构的人能够更清楚地了解这个问题。


更新 2:

尝试设置cache: false

$.ajax({
    url: '/home/ajax', 
    cache: false,
    success: function (result) {
        $('#result').append($('<div/>').html(
            result.startTime + ' | ' + result.endTime
        ));
    }
});
在 IE8 中运行此示例对我来说效果很好。但是在 FF 和 Chrome 中,请求不是并行发送的。我正在使用卡西尼和 VS2010。
2021-03-26 22:21:45
@darin - 我试过$.getJSON()直接去$.ajax(),结果是一样的。
2021-03-28 22:21:45
@darin,不,它没有。在每个浏览器中的结果完全相同。
2021-03-31 22:21:45
@uvita,替换为$.getJSON()$.get()什么不同吗?对我来说,这很重要。使用时$.getJSON请求似乎是排队的。
2021-04-05 22:21:45
@Kev,是的,这有点奇怪。您可以尝试替换$.getJSON()$.get()吗?我用 Cassini 对此进行了测试。我没有安装IIS,无法验证。
2021-04-08 22:21:45