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

Davide Maestroni edited comment on CB-2963 at 4/10/13 11:05 AM:
----------------------------------------------------------------

The issue is present on all Android devices, and it is not actually related to 
the native side but rather to the way JS callbacks are handled. Looking at the 
JS code the behavior is exactly what you would expect, but, from the point of 
view of the JS application, the programmer expects each registered callback to 
be executed in a sequential way rather than mixed together. Unfortunately the 
bug is not easy to replicate and it took me a lot of time to figure out what 
was going on, and it is for that reason that I believe this is a critical 
issue, since it might kick in unexpectedly causing random results.

To work around it I implemented a simple class which postpone the messages 
execution in a timeout. A simplified version is:

{noformat}
var messages = nativeApiProvider.get().exec(service, action, callbackId, 
argsJson);
messageArray.push(messages);
if(!timeoutId) {
    timeoutId = setTimeout(function() {
            timeoutId = null;
            var tempArray = messageArray;
            messageArray = [];
            for(var i = 0; i < tempArray.length; i++) {
                androidExec.processMessages(tempArray[i]);
            }
        }, 0);
}
{noformat}

The above code ensures that each callback is executed (maybe be delayed but...) 
in the correct order, and it is re-entrant, so that it can be called 
recursively without any misbehavior. Moreover, the setTimeout() API exists 
since Javascript was born and, honestly, I don't believe it will show any weird 
behavior in any browser or platform.

In order to replicate the issue you might create a plugin which spawns a thread 
on the Java side repeatedly calling a JS function, let's say:

{noformat}
appView.sendJavascript("navigator.myfunc(" + (++counter) + ");");
{noformat}

And implement the JS function like this:

{noformat}
navigator.myfunc = function(n) {
    console.log("START: " + n);
    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, null, null);
    console.log("END: " + n);
}
{noformat}

Eventually the order of the logs will get messed up.
                
      was (Author: davide):
    The issue is present on all Android devices, and it is not actually related 
to the native side but rather to the way JS callbacks are handled. Looking at 
the JS code the behavior is exactly what you would expect, but, from the point 
of view of the JS application, the programmer expects each registered callback 
to be executed in a sequential way rather than mixed together. Unfortunately 
the bug is not easy to replicate and it took me a lot of time to figure out 
what was going on, and it is for that reason that I believe this is a critical 
issue, since it might kick in unexpectedly causing random results.
To work around it I implemented a simple class which postpone the messages 
execution in a timeout. A simplified version is:

{noformat}
var messages = nativeApiProvider.get().exec(service, action, callbackId, 
argsJson);
messageArray.push(messages);
if(!timeoutId) {
    timeoutId = setTimeout(function() {
            timeoutId = null;
            var tempArray = messageArray;
            messageArray = [];
            for(var i = 0; i < tempArray.length; i++) {
                androidExec.processMessages(tempArray[i]);
            }
        }, 0);
}
{noformat}

The above code ensure that each callback is executed (maybe be delayed but...) 
in the correct order, and it is re-entrant, so that it can be called 
recursively without any misbehavior. Moreover, the setTimeout() API exists 
since Javascript was born and, honestly, I don't believe it will show any weird 
behavior in any browser or platform.

In order to replicate the issue you might create a plugin which spawns a thread 
on the Java side repeatedly calling a JS function, let's say:

{noformat}
appView.sendJavascript("navigator.myfunc(" + (++counter) + ");");
{noformat}

And implement the JS function like this:

{noformat}
navigator.myfunc = function(n) {
    console.log("START: " + n);
    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, null, null);
    console.log("END: " + n);
}
{noformat}

Eventually the order of the logs will get messed up.
                  
> Javascript callback functions are not executed in the correct order
> -------------------------------------------------------------------
>
>                 Key: CB-2963
>                 URL: https://issues.apache.org/jira/browse/CB-2963
>             Project: Apache Cordova
>          Issue Type: Bug
>          Components: Android
>    Affects Versions: 2.5.0
>            Reporter: Davide Maestroni
>            Assignee: Andrew Grieve
>
> I created an application which sends asynchronous notifications from the 
> native to the Javascript side. I noticed that in some cases the Javascript 
> code of the callbacks is not executed in the expected order.
> Let me show an example:
> # a native event trigger a notification to the Javascript side where a 
> callback function (callback1 - part 1) gets correctly called
> # in the while, on the native side, a different notification is triggered
> # inside the Javascript code of the first callback the cordova.exec() API is 
> issued
> # at this point the Javascript code of the second callback (callback2) is 
> executed sequentially on the same thread
> # when the second callback completes, the final part of the code in the first 
> callback (callback1 - part 2) is executed
> So that, if the callback functions are defined as below:
> {noformat}
> function callback1() {
>     <callback1 - part 1>
>     cordova.exec(...);
>     <callback1 - part 2>
> }
> function callback2() {
>     <callback2>
> }
> {noformat}
> the Javascript code gets executed in the following order:
> {noformat}
> <callback1 - part 1>
> cordova.exec(...);
> <callback2>
> <callback1 - part 2>
> {noformat}
> which is not the expected behavior.
> I believe the problem is related to the following lines of code in the 
> _cordova.android.js_ file:
> {noformat}
>         var messages = nativeApiProvider.get().exec(service, action, 
> callbackId, argsJson);
>         androidExec.processMessages(messages);
> {noformat}
> which get called if _jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT_.
> That causes enqueued messages to get executed sequentially before the 
> execution of the first callback is complete.
> An easy fix would be to change the above code into:
> {noformat}
>         var messages = nativeApiProvider.get().exec(service, action, 
> callbackId, argsJson);
>         setTimeout(function() {
>                 androidExec.processMessages(messages);
>         }, 0);
> {noformat}
> so that the enqueued messages are processed in the next Javascript loop.

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