【问题标题】:jQuery $.ajax calls executing synchronously instead of concurrently on calls to WCF servicejQuery $.ajax 调用同步执行而不是同时调用 WCF 服务
【发布时间】:2012-12-20 21:36:50
【问题描述】:

我有一个 Web 应用程序,其中包含一个页面,该页面上的表单包含数百个字段。更改任何字段都会触发 jQuery $.ajax 回调到服务器,以确定是否需要根据新值将任何其他字段添加到表单中。换句话说,当一个字段发生变化时,它会检查是否需要将任何依赖字段添加到表单中。

这个过程有点复杂,因此,有时这些检查可能需要几秒钟的时间。

问题是,如果一个字段处于 $.ajax 调用的中间,而我在等待第一个字段完成时尝试更改另一个字段,则 UI 被阻塞,我无法在页面直到第一次调用完成。就像 UI 一次只能处理一个 $.ajax 调用。这是 $.ajax 调用的副本:

$.ajax({
        type: "POST",
        async: true,
        url: serviceUrl,
        data: JSON.stringify(data),
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        processdata: true,
        success: serviceSuccess,
        error: serviceFailed
    });

serviceSuccess 回调有很多复杂的代码来更新 UI,但是我删除了其中的所有代码,问题仍然存在。我认为这与回调没有任何关系,因为它似乎发生在调用完成之前,但为了确定,我试了一下。

之前有没有人用 jQuery $.ajax 听说过类似的东西?

编辑:我注意到从 $.ajax 调用返回的响应大小约为 130k。

编辑 2: $.ajax 调用正在调用 WCF 服务。类定义现在如下所示:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ReviewDataService : IReviewDataService, IReadOnlySessionState 

解决方案:所以 Jason 肯定为我指明了正确的方向。事实上,我的问题是由于 ASP.NET 会话锁定。为了解决这个问题,我必须从调用堆栈中的任何位置消除对源自我的 WCF 服务的调用的 Session 使用。此外,我将 AspNetCompatibilityRequirementsMode 设置为 AspNetCompatibilityRequirementsMode.NotAllowed 并在 web.config 的 serviceHostingEnvironment 元素中设置 aspNetCompatibilityEnabled="false"。

这导致 WCF 在尝试访问 Session 时抛出错误,并且我重构了每个实例,以便它们不依赖于 session。之后,我的 WCF 调用开始并发运行,而不是同步运行。

【问题讨论】:

  • 似乎有很多数据。它包括什么?
  • 很多,它是任何依赖字段的渲染 HTML,需要根据更改的字段插入到 DOM 中。这是(不幸的是)一个 ASP.NET Web 表单应用程序,我们必须在服务器端呈现控件并通过这些 jQuery 调用将呈现的 HTML 注入回 DOM。在这种情况下,检查了 697 个依赖字段(尽管并非所有这些字段都已呈现)。
  • 我假设你不能进行同步调用?它看起来仍然是大量的 HTML。我想一定有很大的下拉列表。

标签: jquery asp.net ajax


【解决方案1】:

如果您使用的是 ASP.NET,它们会在 Session 对象上使用读/写锁。这使得 AJAX 请求串行。

在 ASP.NET 中,您可以拥有实现 IReadOnlySessionState 的 IHttpHandler,它不会在 Session 上放置读/写锁。不过不确定您使用的是什么环境。

编辑 1

这篇文章似乎表明 WCF 服务不允许您灵活地解锁会话:

How to force an IIS hosted WCF or ASMX [webservice] to use session object readonly?

编辑 2

设置一个不会锁定 Session 对象的 IHttpHandler。

Web.config

<configuration>
    <connectionStrings>
    </connectionStrings>
    <system.web>
        <compilation debug="true" targetFramework="4.0" />
    </system.web>
    <system.webServer>
        <httpProtocol>
            <customHeaders>
                <add name="Access-Control-Allow-Origin" value="*" />
            </customHeaders>
        </httpProtocol>
        <modules runAllManagedModulesForAllRequests="true" />
        <handlers>
            <add name="MyProvider" path="MyProvider.aspx" verb="*" type="MyNamespace.MyProvider" resourceType="Unspecified" />
        </handlers>
    </system.webServer>
</configuration>

MyProvider.cs

using System;
using System.Web;
using System.Web.SessionState;

namespace MyNamespace
{
    public class MyProvider : IHttpHandler, IReadOnlySessionState
    {
        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
        }
    }
}

如果你想创建一个接收和返回 JSON 对象的提供者,你可以为请求和响应创建类,然后你的处理程序可以这样设置:

MyProvider.cs(处理 JSON 对象)

using System;
using System.Runtime.Serialization.Json;
using System.Web;
using System.Web.SessionState;

namespace TestWebsite
{
    public class MyProvider : IHttpHandler, IReadOnlySessionState
    {
        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            var response = new Json.Response();
            try
            {
                var requestSerializer = new DataContractJsonSerializer(typeof(Json.Request));
                var request = (Json.Request)requestSerializer.ReadObject(context.Request.InputStream);

                // ...
            }
            catch (Exception ex)
            {
                response.Error = ex.ToString();
            }

            try
            {
                var responseSerializer = new DataContractJsonSerializer(typeof(Json.Response));
                context.Response.ContentType = "application/json";
                responseSerializer.WriteObject(context.Response.OutputStream, response);
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}

【讨论】:

  • 这是一个 ASP.NET 应用程序。我必须对所有的电话进行分类,看看我们是否在其中任何一个电话中都打到了 Session。值得研究。
  • 您实际上不必使用 Session。默认情况下,ASP.NET 将锁定会话。尝试在您的 Web 服务上实现 IReadOnlySessionState,看看问题是否消失。
  • 我对这个有点希望,但是在 Web 服务上实现 IReadOnlySessionState 并没有任何效果。听起来这仍然有潜力,我会再检查一下。
  • 您是在调用 .ASMX、.APSX、自定义 IHttpHandler 等吗?我们所有的 AJAX 调用都是针对专门充当 Ajax 提供者的自定义 IHttpHandler。我没有尝试让 ASPX 或 ASMX 页面使用 IReadOnlySessionState,所以我无法确切断言它是如何工作的。如果有帮助,我可以提供一个设置 IHttpHandler 的示例。
  • 我还没有完全解决这个问题,但是你的回答给了我一个很好的起点,所以我会把它标记为答案。解决问题后,我将使用解决方案更新我的帖子。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-05
  • 2012-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-21
  • 1970-01-01
相关资源
最近更新 更多