从上家公司就有碰到这种场景,一个页面有个ajax请求,如果后台要执行很久,那么后续的ajax请求,都会在浏览器里排队,直到第一个这被执行出来(或者超时),才会轮到后面的。
这个很坑,明明是异步并行的东西,几乎变成同步串行了,原先搜索过,没找到答案。今天突然在园里发现了一篇文章,解决了心中的困惑。
https://www.cnblogs.com/emrys5/p/aspnet-session-readonly.html
原来是Session的存在,导致默认是一个个按顺序执行的,如果没有Session,那表现出来的就是真正的ajax异步,互不影响。如果有了Session,即使你没在方法里修改或读取任何Session,它也是默认这样锁定的。
解决方法是在控制器类上加SessionState(SessionStateBehavior.ReadOnly),这样就可以了。但有两个疑问:
1、看名称是Session只读了,但我试了也可以修改Session。网上也有人说是可以改。
http://www.it1352.com/261342.html
2、有人说同一Action仍会串行,但我试了加了ReadOnly后,同一Action也是异步并行的。
原来SessionStateModule模块实现了一个写入锁,并对其进行排队处理,所以同一SESSION下的所有请求都会被串行执行。
解决方案也很简单,在页面上增加EnableSessionState="ReadOnly"指令即可。 当然,增加了这个指令后,这个页面也就不能修改SESSION值了。
衍生
GOOGLE的过程中,发现.NET MVC同样也有这个问题,其解决方案是在controller上增加特性[SessionState(SessionStateBehavior.ReadOnly)]
随手实验一番,不加以上特性时,在有SESSION的情况下,同SESSION的请求会被串行处理。增加SessionStateBehavior.ReadOnly后不同action中,请求会被并行处理,但是相同action仍然是串行处理的。
即使是Task<ActionResult>,任然无法逃脱串行执行的魔掌。
不管怎样,如果对Session没有特别要求的页面,又有未优化好SQL或代码的请求在,一时不好优化或推倒重来,那就可以尝试下这种特性。
网上也推荐不使用Session,改用JWT等前后端分离的。但很多旧项目还是基于Session的,这个任何公司都会有旧项目要维护,全做新项目是可遇不可求。
附上测试代码,标红的就是这句特性:
using System.Threading; using System.Web.Mvc; using System.Web.SessionState; namespace TestAjaxPending.Controllers { [SessionState(SessionStateBehavior.ReadOnly)] public class HomeController : Controller { public ActionResult Index() { return View(); } [HttpPost] public ActionResult TestAjax6() { Thread.Sleep(6000); Session["LoginId"] = 6; return Json(new { Code = 6, LoginId = Session["LoginId"] }); } [HttpPost] public ActionResult TestAjax1() { Thread.Sleep(1000); Session["LoginId"] = 1; return Json(new { Code = 1, LoginId = Session["LoginId"] }); } } }
using System.Web.Mvc; namespace TestAjaxPending.Controllers { public class LoginController : Controller { public ActionResult Login() { Session["LoginId"] = 99; return Json(new { Code = Session["LoginId"] }, JsonRequestBehavior.AllowGet); } } }
@{ ViewBag.Title = "About"; } <style> #result6 div { background-color: red; display: inline-block; height: 80px; margin: 10px; width: 60px;} #result1 div { background-color: blue; display: inline-block; height: 80px; margin: 10px; width: 60px;} </style> @section scripts{ <script type="text/javascript"> $(function () { $("#btn-request6").click(function () { $("#result6").empty(); $.ajax({ url: "Home/TestAjax6", type: "post", success: function () { $("#result6").append("<div></div>"); } }); }); $("#btn-request1").click(function () { $("#result1").empty(); $.ajax({ url: "Home/TestAjax1", type: "post", success: function () { $("#result1").append("<div></div>"); } }); }); $("#btn-login").click(function () { $.ajax({ url: "Login/Login", type: "post", success: function (x) { alert('登录成功!'); } }); }); }); </script> } <h3> 模拟操作 </h3> <div> <input type="button" value="模拟登录" id="btn-login" /> <input type="button" value="模拟请求6S" id="btn-request6"/> <input type="button" value="模拟请求1S" id="btn-request1"/> </div> <h3> 结果 </h3> <div id="result6"></div> <div id="result1"></div>