0

I have some code where I wish to call a page method for each row in a datatable. Each row contains user information and the page method looks for additional data regarding that user, in a certain time period. If such data exsts, the idea is to then append the data as a new row to the current row. If no such data exists, move on to the next row.

I begin my code with:

        $.when(GetStartDate(), GetEndDate())
            .then(function () {                    
                GetSchedules();

            })
            .fail(function () {
                failureAlertMsg();
            })

First I retrieve start and end dates via page methods. This works fine. Then I try to call a method for each datarorow in the table:

    function GetSchedules() {
        $('.DataRow').each(function () {
           GetUserSchedule($(this));
        });
    }

This to works no problem. I pass the current data row to a new function which is:

    var currDataRow;
    var currUserID;

    function GetUserSchedule(dr) {
        currDataRow = dr;
        currUserID = currDataRow.find('td').eq(0).text().trim();
        $.ajax({
            type: "POST",
            url: "mypagewithjqueryurl.aspx/GenerateUserSchedule",
            data: "{'StartDate':'" + startDate + "', 'EndDate':'" + endDate + "', 'UserID':'" + currUserID +"'}",    //params
            contentType: "application/json",
            dataType: "json",
            success: function () {
                alert('Succeeded');
            },
            error: AjaxFailed
        });
    }

When I step through the code, the function is called for each row, currDataRow and currUserID is populated as expected, and the ajax call is performed and here is where the problem lies. The call is made but neither success nor error functions are called until the calls are completed for all rows. Then the success method is called for each row but the required data has been lost.

How can I restructure my code so that the success function is called for each ajax request?

Thanks in advance for any insight.

lonesomeday
  • 233,373
  • 50
  • 316
  • 318
Bengal
  • 329
  • 4
  • 21
  • 1
    You might try removing the alert and putting in a console.log. Alert will block all JavaScript until you click okay. – Drew Mar 01 '11 at 17:11

1 Answers1

1

Ajax calls from jquery are asynchronous by default, so it is likely that a handful of calls would all be initiated before any of them succeeds or fails. If you want them to be synchronous, you need to add async: false as a parameter.

You are also limited to two async requests simultaneously, which is also I'm sure a factor here.

This doesn't seem like the best architecture- why not combine all the data into a single request, and set your WebService/PageMethod up so it can handle an array or collection? This is a simpler architecture and will also perform much better than one request per row.

To pass an array, in C# you'd do something like this in your method:

using System.Runtime.Serialization;
...

        [Serializable]
        class Data {
           DateTime StartDate; 
           DateTime EndDate;
        }

    // or really any serializable IEnumerable
        public static MyMethod(string data) {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            Data[] data = (Data[])serializer.Deserialize(data); 
            foreach (Data item in data) {
              // do stuff
            }
        }

in Javascript, make your array (or object), e.g.

    var dates = Array();
    // loop
    var datestruct = {
      StartDate: startDate,
      EndDate: endDate 
    }
    dates[index]=dateStruct;
    // end loop

then for data: in your $.ajax:

$.toJSON(dates)

You could also build the string manually the way you are now, brackets [] delineate array elements. e.g.

"{['StartDate':'"+startDate+"','EndDate':'"+endDate+"'],['StartDate ... ]}"

but why not use something like JSON serializer that does it for you?

Community
  • 1
  • 1
Jamie Treworgy
  • 23,934
  • 8
  • 76
  • 119
  • I don't want them to be synchronous. If that were true, the page would repost for each datarow, would it not? My understanding of the deferred object is that we are allowed to stack up as many asynch requests as we wish in the .when()... am I incorrect in this? I'll look into retructuring as you suggested. I guess I am unsure of how to pass an array of datarows to the pagemethod, and how to cast it back into an array of datarows in the pagemethod itself. – Bengal Mar 01 '11 at 17:39
  • Synchronous means that code execution will halt until the request is complete. Is has no bearing on the page being posted or not. With asynchronous requests, as it's coded now, code execution continues after the request without waiting for the result. But generally, no, you don't want synchronous, because that will cause the client to freeze until the request is completed. What you really want is *one asynchronous request* instead of several. The only reason generally why you'd want multiple async requests would be if they were initiated by entirely unrelated events. – Jamie Treworgy Mar 01 '11 at 17:42
  • Trying it out... give me a few – Bengal Mar 01 '11 at 18:11
  • jamietre, could I trouble you for an example of how to get the values from the array on the C# side? I have the serializable class in place, but not sure how to get the data out of the stringified JSON I sent – Bengal Mar 01 '11 at 18:58
  • Sure - i made an edit. I actually had a mistake before, the parameter for your method is always going to be a string, then you deserialize it. Also look into `json.NET` which is a much more robust serializer than the .NET framework JavascriptSerializer but for this situation, nothing wrong with it. – Jamie Treworgy Mar 01 '11 at 19:08
  • jamietre, thank you so much for all you help and patience. Please answer one more related question. I have never used the JSON.stringify() before... do I need to include this: http://www.json.org/json2.js ? I ask because I get a 'JSON is undefined' error – Bengal Mar 01 '11 at 19:23
  • jamietre, I have includes the file for JSON and have added the include statement for the file as well, yet when I hit the line containing JSON.stringify(dates) it keeps bombing out with 'JSON is undefined. Any ideas or suggestions?' Do I need to initialize a JSON object or something? – Bengal Mar 01 '11 at 20:36
  • My bad. use `$.toJSON` with the jQuery json plugin. JSON.stringify is a native function but it is not supported by all browsers. JSON.toJSON will use stringify if it's available. – Jamie Treworgy Mar 01 '11 at 20:46
  • jamietre, Thank you for your feedback/help. I'll mark you as having answered. Currently I am unable to pass the stringified argument to the page method successfully... if I call it without the parameter, I can step into it, but I suppose I can manually piece it together. The page methood accepts one string, and the stringified result is in the form of "{" + jsonText + "}" where the jsonText contains [{"StartDate":"3/1/20011", "EndDate":"3/31/2011", "Userd":"8", ...}, {more stuff}]. I shouldn't be using just one string parameter in the page method. Thanks for the guidance. I appreciate it. – Bengal Mar 02 '11 at 14:57
  • Are you saying that the method won't run at all? If there is a problem with the format of the JSON string, that shouldn't cause an error until you try to deserialize it. The format looks correct. I assume that's supposed to be UserID, not UserD :) – Jamie Treworgy Mar 02 '11 at 15:28
  • Right, the call to the page method always fails and the .error function executes. If I remove the data or pass a simple string the page method executes. But if I pass the stringified array it doesn't. The page method signature is : – Bengal Mar 02 '11 at 15:38
  • '[System.Web.Script.Services.WebMethod] public static void GenerateUserScheduls(string data)' I also tried string[] data but no luck. One side note, the serializer namespace is System.Web.Script.Serialization.JavaScriptSerializer – Bengal Mar 02 '11 at 15:46
  • Try this. Add [ScriptMethod(ResponseFormat = ResponseFormat.Xml )], (Yes, XML), and change the type to `string[]'. I may be getting confused about the default handling using a method, it will deserialize automatically and the default format may not be correct. The return type should always be a serialized string, if you return data. If that doesn't work, and you want to post the source code somewhere I will take a look at it. – Jamie Treworgy Mar 02 '11 at 16:26
  • Scratch that... you aren't returning data. ResponseFormat won't matter. But the parm type should be string[], not string. I am not sure why it's not working, it's hard to piece it all together in my head looking at bits of code, so if you want to post it somewhere I'd be happy to take a look. – Jamie Treworgy Mar 02 '11 at 16:30
  • Arhg, I think this is me telling you the wrong thing again. Just add [ScriptMethod] attribute, and set the parameter as `Data[]` --whatever the structure/class is that you're using to describe the array elements. [ScriptMethod] ought to cause it to be deserialized from JSON automatically. I am not sure why just string (without that flag) didn't work but this is how I have usually done it. – Jamie Treworgy Mar 02 '11 at 16:37
  • I put the code as a new question here... didn't know how else to do it: http://stackoverflow.com/questions/5171287/trouble-passing-json-stringified-array-ro-pagemethod – Bengal Mar 02 '11 at 17:43