[
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