GWT/JSNI-“;DataCloneError-无法克隆对象";-如何调试?
我试图通过JSNI调用parallels.js。Parallels为web工作者提供了一个很好的API,我编写了一些轻量级包装器代码,它为来自GWT的工作者提供了比Elemental更方便的接口。然而,我遇到了一个让我难堪的错误: com.google.gwt.core.client.JavaScriptException:(DataCloneError)@io.mywrapper.workers.Parallel::runParallel([Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)([Java object:[Ljava.lang.String;@1922352522,JavaScript object(3006),JavaScript object(3008)]):无法克隆对象 在托管模式下,这来自: com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:249)com.google.gwt.dev.shell.modulespaceophm.doInvoke(modulespaceophm.java:136)com.google.gwt.dev.shell.ModuleSpace.invokenactive(ModuleSpace.java:571)com.google.gwt.shell.ModuleSpace.invokenativeoid(ModuleSpace.java:299)com.google.gwt.dev.shell.JavaScriptHost.invokeNativeVoid(JavaScriptHost.java:107),io.mywrapper.workers.Parallel.runParallel(Parallel.java) 这是我的密码: 创建工作进程的客户端调用示例:GWT/JSNI-“;DataCloneError-无法克隆对象";-如何调试?,gwt,jsni,Gwt,Jsni,我试图通过JSNI调用parallels.js。Parallels为web工作者提供了一个很好的API,我编写了一些轻量级包装器代码,它为来自GWT的工作者提供了比Elemental更方便的接口。然而,我遇到了一个让我难堪的错误: com.google.gwt.core.client.JavaScriptException:(DataCloneError)@io.mywrapper.workers.Parallel::runParallel([Ljava/lang/String;Lcom/goo
Workers.spawnWorker(new String[]{"hello"}, new Worker() {
@Override
public String[] work(String[] data) {
return data;
}
@Override
public void done(String[] data) {
int i = data.length;
}
});
提供通用接口的API:
public class Workers {
public static void spawnWorker(String[] data, Worker worker) {
Parallel.runParallel(data, workFunction(worker), callbackFunction(worker));
}
/**
* Create a reference to the work function.
*/
public static native JavaScriptObject workFunction(Worker worker) /*-{
return worker == null ? null : $entry(function(x) {
worker.@io.mywrapper.workers.Worker::work([Ljava/lang/String;)(x);
});
}-*/;
/**
* Create a reference to the done function.
*/
public static native JavaScriptObject callbackFunction(Worker worker) /*-{
return worker == null ? null : $entry(function(x) {
worker.@io.mywrapper.workers.Worker::done([Ljava/lang/String;)(x);
});
}-*/;
}
工人:
public interface Worker extends Serializable {
/**
* Called to perform the work.
* @param data
* @return
*/
public String[] work(String[] data);
/**
* Called with the result of the work.
* @param data
*/
public void done(String[] data);
}
最后是Parallels包装器:
public class Parallel {
/**
* @param data Data to be passed to the function
* @param work Function to perform the work, given the data
* @param callback Function to be called with result
* @return
*/
public static native void runParallel(String[] data, JavaScriptObject work, JavaScriptObject callback) /*-{
var p = new $wnd.Parallel(data);
p.spawn(work).then(callback);
}-*/;
}
这是什么原因造成的
JSNI文档说,关于阵列:
opaque value that can only be passed back into Java code
这非常简洁,但最终我的数组会被传递回Java代码,所以我认为这些都是可以的
编辑-好的,错误的假设。尽管数组表面上只是被传递回Java代码,但它导致了错误(这很奇怪,因为DataCloneError上几乎没有googleability)将它们更改为字符串可以工作;但是,字符串不足以满足我在这里的需要。看起来对象与数组面临相同的问题;我在另一个StackOverflow线程中看到Thomas对JSArrayUtils的引用,但我不知道如何使用字符串数组调用它(它需要一个JavaScriptObjects数组作为非原语类型的输入,这对我没有好处。)有没有一个简单的解决方法
编辑2-更改为在我使用字符串[]的地方使用JSArrayString。新问题;这次没有stacktrace,但在控制台中我得到错误:Uncaught ReferenceError:_gwt_makeJavaInvoke未定义。当我在开发工具中单击生成脚本的url时,我得到以下代码段:
self.onmessage = function(e) {self.postMessage((function (){
try {
return __gwt_makeJavaInvoke(3)(null, 65626, jsFunction, this, arguments);
}
catch (e) {
throw e;
}
})(e.data))}
我看到_gwt_makeJavaInvoke是JSNI类的一部分;那么为什么找不到它呢?如果您从未使用开发模式,那么现在可以安全地假装Java字符串[]
是一个包含字符串的JS数组。这将在开发模式下中断,因为数组必须在Java中可用,并且字符串是经过特殊处理的,如果编译器以不同方式优化数组,将来可能会中断
未来可能出现错误的情况:
Java数组和JavaScript数组的语义是不同的-Java数组不能调整大小,并且根据组件类型(数组中的数据)使用特定值初始化.由于您正在编写Java代码,编译器可能会根据您如何创建和使用该数组的详细信息做出假设,这些假设可能会被JS代码破坏,而JS代码不知道永远不要修改该数组
一些基本类型的数组可以优化为JavaScript中的TypeDarray,在大小调整方面更接近Java语义,在分配方面更接近Java行为。这也会提高性能,但可能会破坏int[]
,double[]
等的任何使用
相反,您应该将数据复制到
JsArrayString
,或者只使用js数组来保存数据,而不是来回移动,这取决于您的用例。各种JsArray类型可以调整大小,并且已经作为js外部可以理解和使用的JavaScript对象存在
对编辑2的答复: 据猜测,parallel.js脚本正试图从webworker中的另一个作用域运行代码(这就是代码的要点,对吧),而您的GWT代码不存在。因此,它无法调用makeJavaInvoke,这是返回到开发模式的桥梁(对于已编译的js来说可能是另一个失败)。根据需要,传递的回调必须满足特定的要求才能传递到
spawn
,或者then
——您的匿名函数肯定不满足这些要求,并且可能无法维护java语义
在我深入了解之前,先看看我刚才回答的关于webworkers和gwt/java的基本问题的答案:
如上所述,WebWorkers实际上是新的进程,与原始进程没有共享代码或共享状态。Parallel.js代码试图用一点技巧来掩盖这一点-共享状态仅以传递给原始并行构造函数的内容的形式提供,但您正试图传递ins“java”对象及其调用方法的实例。这些java实例具有自己的状态,并且可能通过Worker实例中的字段链接回java应用程序的其余部分。如果我正在实现Worker并执行引用其他数据而不是传入的数据的操作,那么我将看到更多奇怪的失败
因此,传入的函数必须是完全独立的-它们不能以任何方式引用外部代码,因为这样就不能将函数传递给webworker或几个webworker,每个webworker都不知道彼此的存在。例如,请参阅:
这是不可能的,因为它会
- 需要在工作人员之间共享状态
- 要求我们传输所有作用域变量(我认为甚至不可能读取可用的作用域)