You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@cordova.apache.org by "Maris Seimanovs (JIRA)" <ji...@apache.org> on 2014/05/22 16:39:03 UTC

[jira] [Commented] (CB-4873) XHRHelper is failing with simultaneous asynchronous requests

    [ https://issues.apache.org/jira/browse/CB-4873?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14005959#comment-14005959 ] 

Maris Seimanovs commented on CB-4873:
-------------------------------------

The problem is still relevant and can be reproduced on Windows Phone 8.1 (using emulator or real device with 8.1 developer preview) with a simple page with two sequential AJAX requests. Our team spent many hours trying to find what's going on and where our AJAX requests are messed up when we load multiple page parts through AJAX.

Here is a simplified code example to test.

index.html in application root:

{code:html}
    <!DOCTYPE html>
    <html>
        <head>
            <title>Mobile sandbox</title>
            <meta charset="UTF-8">
            <script type="text/javascript" src="libs/jquery/jquery.min.js"></script>
        </head>
        <body>
    
            <div id="redbox" style="width:100px;height:100px;background-color:red;">
            </div>
    
            <div id="greenbox" style="width:100px;height:100px;background-color:green;">
            </div>
             
            <script>
                $(function () {
    
                    alert("Requesting data for redbox...");
    
                    $.ajax("test/redText.html")
                      .done(function (text) {
                          alert("Filling redbox with contents " + text);
                          $("#redbox").html(text);
                      })
                      .fail(function () {
                          alert("Error in redbox");
                      })
                      .always(function () {
                          alert("Complete redbox");
                      });
    
                    alert("Requesting data for greenbox...");
    
                    $.ajax("test/greenText.html")
                      .done(function (text) {
                          alert("Filling greenbox with contents " + text);
                          $("#greenbox").html(text);
                      })
                      .fail(function () {
                          alert("Error in greenbox");
                      })
                      .always(function () {
                          alert("Complete greenbox");
                      });
    
                });
            </script>
        </body>
    </html>
{code}

test/greenText.html:
{code:html}
    <span>GREEN</span>
{code}
test/redText.html:
{code:html}
    <span>RED</span>
{code}

The only dependency to run this test is jQuery which I have put in libs/jquery/ folder.

When I deploy the same code to Windows Phone as Cordova app, the `redbox` request never receives any data, nor any errors. The `greenbox` request receives data of `redbox`, and thus we have empty red box and green box with text "RED" in it.
Here is the sequence of alerts:
{code}
    Requesting data for redbox...
    Requesting data for greenbox...
    Filling greenbox with contents <span>RED</span>
    Complete greenbox
{code}

I have no idea why it's the case - maybe Microsoft has changed something in Windows 8.1 and now the MiniBrowser_ScriptNotify calls are completely async. I know that WP 8.1 is not RTM yet but nevertheless I think the fix should be implemented anyway because it seems more safe and correct solution to have a separate callback function for each request instead of the current single window.__onXHRLocalCallback.

> 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 was sent by Atlassian JIRA
(v6.2#6252)