[ 
https://issues.apache.org/jira/browse/CB-4873?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Jonathan Naguin updated CB-4873:
--------------------------------

    Description: 
XHRHelper is failing in processing mutiple simultaneous asynchronous AJAX 
requests. I am using the latest code from 
https://github.com/apache/cordova-wp8/blob/master/wp8/template/cordovalib/XHRHelper.cs

The problem is related with {{_onXHRLocalCallback}} which is save into the 
{{window}} object as a unique function. When, for example, two Ajax requests 
are evaluated at same time, the last {{funk}} function overrides the first 
{{_onXHRLocalCallback}} without receive the data from the C# code to that 
particular request.

To demostrate this I put {{console.log("XHR: " + resolvedUrl);}} inside 
{{__onXHRLocalCallback}} and 
{{System.Diagnostics.Debug.WriteLine("HandleCommand: " + url);}} in 
{{HandleCommand}} method (my code uses *Require JS* to load this resources). 
The output is this:

{code}
HandleCommand: x-wmapp0:www/src/modules/home/HomeView.html
HandleCommand: x-wmapp0:www/src/modules/orders/OrdersView.html
XHR: x-wmapp0:www/src/modules/orders/OrdersView.html
XHR: x-wmapp0:www/src/modules/orders/OrdersView.html
XHR: HandleCommand: x-wmapp0:www/src/modules/order/OrderDetailView.html
XHR: x-wmapp0:www/src/modules/order/OrderDetailView.html
{code}

As you can see, one request is missing: "HomeView.html".

h6. NOTES
- If I set {{false}} the {{this.isAsync}} variable it works (this way it is 
executed without using setTimeout).
- If I put a console.log before launch {{funk}} it works.
- It works on the simulator, but it fails on a real device.

h6. Possible solution

In conclusion, I assumed that it's a timing problem. To resolve it I decided to 
save a onXHRLocalCallback function per each request:


{code}
var funk = function () {

    if (! window.__onXHRLocalCallback){
        window.__onXHRLocalCallback = {}; //Object to store the functions
    }
    
    window.__onXHRLocalCallback[resolvedUrl] = function (responseCode, 
responseText) {
        alias.status = responseCode;
        if (responseCode == '200') {
            alias.responseText = responseText;
        }
        else {
            alias.onerror && alias.onerror(responseCode);
        }

        alias.changeReadyState(XHRShim.DONE);
        delete window.__onXHRLocalCallback[resolvedUrl]; //Delete the function
    }
    alias.changeReadyState(XHRShim.LOADING);
    window.external.Notify('XHRLOCAL/' + resolvedUrl);
}
{code}

So I had to change in {{HandleCommand}} method the way of invoking this 
callback. I decided to create a helper function to be called in each case:

{code}
/// <summary>
/// Invoke a XHR callback
/// </summary>
/// <param name="url">The URL of the request</param>
/// <param name="code">The response code</param>
/// <param name="text">The response text</param>
private void InvokeCallback(string url, int code, string text)
{
    string args = string.Format("('{0}', {1}, {2});", code, 
WPCordovaClassLib.Cordova.JSON.JsonHelper.Serialize(text), 
WPCordovaClassLib.Cordova.JSON.JsonHelper.Serialize(url));
    string callback = @"(function(code, text, url){
        try {
            window.__onXHRLocalCallback[ url ].call(null, code, text);
        }
        catch(e) {
            console.log('Error calling method from XHRHelper :: ' + e);
        }
    })" + args;
    Browser.InvokeScript("eval", new string[] { callback });
}
{code}

To be called as {{InvokeCallback(url, 200, text);}} or {{InvokeCallback(url, 
404, null);}}

Thanks.

  was:
XHRHelper is failing in processing mutiple simultaneous asynchronous AJAX 
requests.

The problem is related with {{_onXHRLocalCallback}} which is save into the 
{{window}} object as a unique function. When, for example, two Ajax requests 
are evaluated at same time, the last {{funk}} function overrides the first 
{{_onXHRLocalCallback}} without receive the data from the C# code to that 
particular request.

To demostrate this I put {{console.log("XHR: " + resolvedUrl);}} inside 
{{__onXHRLocalCallback}} and 
{{System.Diagnostics.Debug.WriteLine("HandleCommand: " + url);}} in 
{{HandleCommand}} method (my code uses *Require JS* to load this resources). 
The output is this:

{code}
HandleCommand: x-wmapp0:www/src/modules/home/HomeView.html
HandleCommand: x-wmapp0:www/src/modules/orders/OrdersView.html
XHR: x-wmapp0:www/src/modules/orders/OrdersView.html
XHR: x-wmapp0:www/src/modules/orders/OrdersView.html
XHR: HandleCommand: x-wmapp0:www/src/modules/order/OrderDetailView.html
XHR: x-wmapp0:www/src/modules/order/OrderDetailView.html
{code}

As you can see, one request is missing: "HomeView.html".

h6. NOTES
- If I set {{false}} the {{this.isAsync}} variable it works (this way it is 
executed without using setTimeout).
- If I put a console.log before launch {{funk}} it works.
- It works on the simulator, but it fails on a real device.

h6. Possible solution

In conclusion, I assumed that it's a timing problem. To resolve it I decided to 
save a onXHRLocalCallback function per each request:


{code}
var funk = function () {

    if (! window.__onXHRLocalCallback){
        window.__onXHRLocalCallback = {}; //Object to store the functions
    }
    
    window.__onXHRLocalCallback[resolvedUrl] = function (responseCode, 
responseText) {
        alias.status = responseCode;
        if (responseCode == '200') {
            alias.responseText = responseText;
        }
        else {
            alias.onerror && alias.onerror(responseCode);
        }

        alias.changeReadyState(XHRShim.DONE);
        delete window.__onXHRLocalCallback[resolvedUrl]; //Delete the function
    }
    alias.changeReadyState(XHRShim.LOADING);
    window.external.Notify('XHRLOCAL/' + resolvedUrl);
}
{code}

So I had to change in {{HandleCommand}} method the way of invoking this 
callback. I decided to create a helper function to be called in each case:

{code}
/// <summary>
/// Invoke a XHR callback
/// </summary>
/// <param name="url">The URL of the request</param>
/// <param name="code">The response code</param>
/// <param name="text">The response text</param>
private void InvokeCallback(string url, int code, string text)
{
    string args = string.Format("('{0}', {1}, {2});", code, 
WPCordovaClassLib.Cordova.JSON.JsonHelper.Serialize(text), 
WPCordovaClassLib.Cordova.JSON.JsonHelper.Serialize(url));
    string callback = @"(function(code, text, url){
        try {
            window.__onXHRLocalCallback[ url ].call(null, code, text);
        }
        catch(e) {
            console.log('Error calling method from XHRHelper :: ' + e);
        }
    })" + args;
    Browser.InvokeScript("eval", new string[] { callback });
}
{code}

To be called as {{InvokeCallback(url, 200, text);}} or {{InvokeCallback(url, 
404, null);}}

Thanks.

    
> XHRHelper is failing with simultaneous asynchronous requests
> ------------------------------------------------------------
>
>                 Key: CB-4873
>                 URL: https://issues.apache.org/jira/browse/CB-4873
>             Project: Apache Cordova
>          Issue Type: Bug
>          Components: WP7, WP8
>    Affects Versions: 3.0.0
>         Environment: Any
>            Reporter: Jonathan Naguin
>            Assignee: Jesse MacFadyen
>            Priority: Critical
>              Labels: WP8, ajax, asynchronous, multiple, xhrhelper
>
> XHRHelper is failing in processing mutiple simultaneous asynchronous AJAX 
> requests. I am using the latest code from 
> https://github.com/apache/cordova-wp8/blob/master/wp8/template/cordovalib/XHRHelper.cs
> The problem is related with {{_onXHRLocalCallback}} which is save into the 
> {{window}} object as a unique function. When, for example, two Ajax requests 
> are evaluated at same time, the last {{funk}} function overrides the first 
> {{_onXHRLocalCallback}} without receive the data from the C# code to that 
> particular request.
> To demostrate this I put {{console.log("XHR: " + resolvedUrl);}} inside 
> {{__onXHRLocalCallback}} and 
> {{System.Diagnostics.Debug.WriteLine("HandleCommand: " + url);}} in 
> {{HandleCommand}} method (my code uses *Require JS* to load this resources). 
> The output is this:
> {code}
> HandleCommand: x-wmapp0:www/src/modules/home/HomeView.html
> HandleCommand: x-wmapp0:www/src/modules/orders/OrdersView.html
> XHR: x-wmapp0:www/src/modules/orders/OrdersView.html
> XHR: x-wmapp0:www/src/modules/orders/OrdersView.html
> XHR: HandleCommand: x-wmapp0:www/src/modules/order/OrderDetailView.html
> XHR: x-wmapp0:www/src/modules/order/OrderDetailView.html
> {code}
> As you can see, one request is missing: "HomeView.html".
> h6. NOTES
> - If I set {{false}} the {{this.isAsync}} variable it works (this way it is 
> executed without using setTimeout).
> - If I put a console.log before launch {{funk}} it works.
> - It works on the simulator, but it fails on a real device.
> h6. Possible solution
> In conclusion, I assumed that it's a timing problem. To resolve it I decided 
> to save a onXHRLocalCallback function per each request:
> {code}
> var funk = function () {
>     if (! window.__onXHRLocalCallback){
>         window.__onXHRLocalCallback = {}; //Object to store the functions
>     }
>     
>     window.__onXHRLocalCallback[resolvedUrl] = function (responseCode, 
> responseText) {
>         alias.status = responseCode;
>         if (responseCode == '200') {
>             alias.responseText = responseText;
>         }
>         else {
>             alias.onerror && alias.onerror(responseCode);
>         }
>         alias.changeReadyState(XHRShim.DONE);
>         delete window.__onXHRLocalCallback[resolvedUrl]; //Delete the function
>     }
>     alias.changeReadyState(XHRShim.LOADING);
>     window.external.Notify('XHRLOCAL/' + resolvedUrl);
> }
> {code}
> So I had to change in {{HandleCommand}} method the way of invoking this 
> callback. I decided to create a helper function to be called in each case:
> {code}
> /// <summary>
> /// Invoke a XHR callback
> /// </summary>
> /// <param name="url">The URL of the request</param>
> /// <param name="code">The response code</param>
> /// <param name="text">The response text</param>
> private void InvokeCallback(string url, int code, string text)
> {
>     string args = string.Format("('{0}', {1}, {2});", code, 
> WPCordovaClassLib.Cordova.JSON.JsonHelper.Serialize(text), 
> WPCordovaClassLib.Cordova.JSON.JsonHelper.Serialize(url));
>     string callback = @"(function(code, text, url){
>       try {
>           window.__onXHRLocalCallback[ url ].call(null, code, text);
>       }
>       catch(e) {
>           console.log('Error calling method from XHRHelper :: ' + e);
>       }
>     })" + args;
>     Browser.InvokeScript("eval", new string[] { callback });
> }
> {code}
> To be called as {{InvokeCallback(url, 200, text);}} or {{InvokeCallback(url, 
> 404, null);}}
> Thanks.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira

Reply via email to