Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery $.ajax calls executing synchronously instead of concurrently on calls to WCF service

I have a web application that contains a page with a form that has hundreds of fields on it. Changing any of the fields fires a jQuery $.ajax call back to the server to determine if any other fields need to be added to the form based on it's new value. In other words, it checks to see if any dependent fields need to be added to the form when one field changes.

There is a bit of complexity around this process and as a result, sometimes these checks can take a few seconds.

The problem is, if one field is in the middle of its $.ajax call, and I attempt to change another field while waiting for the first one to complete, the UI gets blocked and I can't do anything on the page until the first call completes. It's like the UI can only handle one $.ajax call at a time. Here's a copy of the $.ajax call:

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

The serviceSuccess callback has a lot of complex code to update the UI, but I deleted ALL of the code within it and the problem still occurs. I figured it wasn't anything to do with the callback since it seems to happen before the call completes, but just to be sure, I gave it a shot.

Has anyone heard of anything like this before with jQuery $.ajax?

EDIT: I noticed the size of the response coming back from the $.ajax call is about 130k.

Edit 2: The $.ajax calls are making calls to a WCF service. The class definition now looks like this:

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

SOLUTION: So Jason definitely pointed me in the right direction. My problem was, in fact, due to ASP.NET session locking. In order to get around this, I had to eliminate the use of Session from anywhere in the call stack on calls that originated from my WCF service. Additionally, I set AspNetCompatibilityRequirementsMode to AspNetCompatibilityRequirementsMode.NotAllowed and I set aspNetCompatibilityEnabled="false" in the serviceHostingEnvironment element in web.config.

This caused WCF to throw errors when an attempt was made to access Session and I juts refactored each of those instances so that they didn't rely on session. After that, my WCF calls started running concurrently instead of synchronously.

like image 594
Scott Avatar asked Dec 20 '12 21:12

Scott


1 Answers

If you are using ASP.NET they use read/write locks on the Session object. This makes AJAX request serial.

In ASP.NET you can have IHttpHandlers that implement IReadOnlySessionState which will not place the read/write lock on Session. Not sure what environment you are using though.

Edit 1

This post seems to indicate that WCF services don't allow you the flexibility to unlock the session:

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

Edit 2

Setting up an IHttpHandler that will not lock the Session object.

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

If you want to create a provider that receives and returns JSON objects you can create classes for the Request and Response and then your handler can be set up like this:

MyProvider.cs (processing JSON objects)

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;
            }
        }
    }
}
like image 70
Jason Whitted Avatar answered Oct 06 '22 01:10

Jason Whitted