cordova-issues mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Maris Seimanovs (JIRA)" <j...@apache.org>
Subject [jira] [Commented] (CB-4873) XHRHelper is failing with simultaneous asynchronous requests
Date Thu, 22 May 2014 14:39:03 GMT

    [ 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)

Mime
View raw message