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

几天前我问了这个问题:

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

我在同一个控制器操作中几乎同时发出6个 jQuery 异步 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

但是浏览器重定向到新页面仍然需要一段时间。

Ajax 请求真的是异步的吗? 还是因为 javascript 实际上是单线程的?

我的请求是不是花了太长时间才起作用?

这个问题在 Firefox 和 IE 中也会出现。

我还修改了脚本,直接使用 $.ajax并显式地设置 async: true

我在 IIS7.5上运行这个程序,Windows 2008R2和 Windows 7的风格做同样的事情。

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

40968 次浏览

我试图复制这个,但是没有成功,这是我的测试:

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 操作是并行命中的。


更新:

在我最初的测试中,当使用 $.getJSON()时,请求确实在 FireFox 3.6.12和 Chrome 8.0.552.215中排队。它在 IE8中工作得很好。我的测试是使用 ASP.NET MVC 2项目,VS2010,Cassini 网络服务器,Windows 7 x64位完成的。

现在,如果我用 $.get()替换 $.getJSON(),它在所有浏览器下都能正常工作。这使我相信,这个 $.getJSON()可能存在一些问题,这些问题可能导致请求排队。也许更熟悉 jQuery 框架内部的人能够对此问题有更多的了解。


更新2:

试着设置 cache: false:

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

答案就在我眼前。

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 中阻塞请求