我不认为我理解你问题的各个方面,但无论如何我会尽力提供帮助。请注意,我不了解 PrimeFaces,所以我所做的就是阅读文档。
我的理解是,你试图摆脱全局变量。但恐怕,我认为这是不可能的。
这里的问题是,PrimeFaces 不允许您将远程调用的调用进一步透明地传递给oncomplete
调用(除非您将其传递给 Bean 的 Java 代码,然后返回到 UI,而这通常不是您想要的)。
然而,我希望,你能非常接近它。
第1部分,JS提前回归
另请注意,人们可能对 Java 和 JavaScript 存在一些误解。
Java 是多线程的,并行运行多个命令,而 JavaScript 是单线程的,通常从不等待某些事情完成。要获得响应式 Web-UI,必须异步执行操作。
因此你的remoteCommand
调用(从 JS 端来看)将(通常是异步情况)早于oncomplete
处理程序将被调用。这意味着,如果window[msg]()
返回,你还没有完成remoteCommand
yet.
那么你想用下面的代码来管理什么
if (window[msg]) {
window[msg]();
//Do something to call notifyAll() on oncomplete of remote command.
dosomethinghere();
}
将失败。dosomethinghere()
当remoteCommand
返回(因为 JS 不想等待某些事件,这可能永远不会发生)。这意味着,dosomethinghere()
当 Ajax 请求刚刚打开到远程(Java 应用程序)时将被调用。
要在 Ajax 调用完成后运行某些内容,必须在oncomplete
例行公事(或onsuccess
)。这就是它存在的原因。
第 2 部分,验证msg
请注意一些不同的事情window[msg]()
。如果您不能完全信任推送的消息,这可能会被认为有点危险。window[msg]()
本质上运行以变量内容命名的任何函数msg
。例如如果msg
碰巧是close
then window.close()
将运行,这可能不是您想要的。
你应该确保,msg
是一个预期词,并拒绝所有其他词。示例代码:
var validmsg = { updateModel:1, rc:1 }
[..]
if (validmsg[msg] && window[msg])
window[msg]();
第 3 部分:如何并行处理多个 JSON 消息
全局变量有一些缺点。只有一个。如果您碰巧在 WebSocket 上收到另一条 JSON 消息,而前一条消息仍在处理中remoteCommand
,这将覆盖之前的消息。所以notifyAll()
会看到新的消息两次,旧的消息丢失。
经典的竞争条件。你必须做的是,创建一个类似注册表的东西来注册所有消息,然后将一些值传递给notifyAll()
告知应处理哪些注册消息。
只需进行一点点更改,您就可以并行(此处)或串行(第 4 部分)处理消息。
首先,创建一个计数器来区分消息。也是一个存储所有消息的对象。我们声明我们期望的所有有效消息(参见第 2 部分):
var jsonMsgNr = 0;
var jsonMessages = {};
var validmsg = { updateModel:1 }
现在,每次我们收到一条消息时都添加一条消息:
if (window.WebSocket) {
var ws = new WebSocket("wss://localhost:8181/ContextPath/AdminPush");
ws.onmessage = function (event) {
var jsonMsg = event.data;
var json = JSON.parse(jsonMsg);
var msg=json["jsonMessage"];
if (validmsg[msg] && window[msg]) {
var nr = ++jsonMsgNr;
jsonMessages[nr] = { jsonMsg:jsonMsg, json:json };
为了能够通过nr
to NotifyAll()
需要向 Bean 传递一个附加参数。我们就这样称呼它吧msgNr
:
// Following might look a bit different on older PrimeFaces
window[msg]([{name:'msgNr', value:nr}]);
}
}
}
也许看看https://stackoverflow.com/a/7221579/490291有关以这种方式传递值的更多信息。
The remoteAction
bean 现在获得一个附加参数msgNr
已通过,必须通过 Ajax 传回。
不幸的是,我不知道(抱歉)这在 Java 中是什么样子。因此请确保您对 AjaxCall 的回答复制了msgNr
又出来了。
另外,由于文档对这个主题很安静,我不确定参数如何传递回oncomplete
处理程序。根据 JavaScript 调试器,notifyAll()
获取3个参数:xhdr
, payload
, and pfArgs
。不幸的是,我无法设置测试用例来了解情况如何。
因此,该函数看起来有点像(请耐心等待):
function notifyAll(x, data, pfArgs) {
var nr = ???; // find out how to extract msgNr from data
var jsonMsg = jsonMessages[nr].jsonMsg;
var json = jsonMessages[nr].json;
jsonMessages[nr] = null; // free memory
sendMessage(jsonMsg);
dosomething(json);
}
如果将其分成两个函数,那么您可以调用notifyAll()
来自应用程序中的其他部分:
function notifyAll(x, data, unk) {
var nr = ???; // find out how to extract msgNr from data
realNotifyAll(nr);
}
function realNotifyAll(nr) {
if (!(nr in jsonMessages)) return;
var jsonMsg = jsonMessages[nr].jsonMsg;
var json = jsonMessages[nr].json;
delete jsonMessages[nr]; // free memory
sendMessage(jsonMsg);
dosomething(json);
}
这里有些东西有点多余。例如,您可能不需要json
元素在jsonMessages
或者想要解析json
再次节省一些内存,以防 json 很大。然而,该代码并不是最优的,而是易于根据您的需求进行调整的。
第 4 部分:序列化请求
现在进行序列化的更改。通过添加一些信号量这非常容易。 JavaScript 中的信号量只是变量。这是因为只有一个全局线程。
var jsonMsgNr = 0;
var jsonMessages = {};
var validmsg = { updateModel:1 }
var jsonMsgNrLast = 0; // ADDED
if (window.WebSocket) {
var ws = new WebSocket("wss://localhost:8181/ContextPath/AdminPush");
ws.onmessage = function (event) {
var jsonMsg = event.data;
var json = JSON.parse(jsonMsg);
var msg=json["jsonMessage"];
if (validmsg[msg] && window[msg]) {
var nr = ++jsonMsgNr;
jsonMessages[nr] = { jsonMsg:jsonMsg, json:json };
if (!jsonMsgNrLast) { // ADDED
jsonMsgNrLast = nr; // ADDED
window[msg]([{name:'msgNr', value:nr}]);
}
}
}
}
function realNotifyAll(nr) {
if (!(nr in jsonMessages)) return;
var jsonMsg = jsonMessages[nr].jsonMsg;
var json = jsonMessages[nr].json;
delete jsonMessages[nr]; // free memory
sendMessage(jsonMsg);
dosomething(json);
// Following ADDED
nr++;
jsonMsgNrLast = 0;
if (nr in jsonMessages)
{
jsonMsgNrLast = nr;
window[jsonMessages[nr].json.msg]([{name:'msgNr', value:nr}]);
}
}
Note: jsonMsgNrLast
可能只是一个标志(真/假)。然而,将当前处理的数字放在变量中也许可以在其他地方有所帮助。
话虽如此,如果出现故障,就会出现饥饿问题sendMessage
or dosomething
。所以也许你可以稍微交错一下:
function realNotifyAll(nr) {
if (!(nr in jsonMessages)) return;
var jsonMsg = jsonMessages[nr].jsonMsg;
var json = jsonMessages[nr].json;
delete jsonMessages[nr]; // free memory
nr++;
jsonMsgNrLast = 0;
if (nr in jsonMessages)
{
jsonMsgNrLast = nr;
// Be sure you are async here!
window[jsonMessages[nr].json.msg]([{name:'msgNr', value:nr}]);
}
// Moved, but now must not rely on jsonMsgNrLast:
sendMessage(jsonMsg);
dosomething(json);
}
这样AJAX请求已经发送出去了sendMessage
在跑。如果现在dosomething
有 JavaScript 错误或类似错误,消息仍然可以正确处理。
请注意:所有这些都是在未经任何测试的情况下输入的。可能存在语法错误或更糟的情况。对不起,我已经尽力了。如果您发现错误,编辑就是您的朋友。
第五部分:JS 直接调用
现在,所有这些都已就位并进行了序列化运行,您始终可以调用之前的notifyAll()
using realNotifyAll(jsonMsgNrLast)
。或者您可以显示jsonMessages
在列表中并选择任意数字。
通过跳过呼叫window[jsonMessages[nr].json.msg]([{name:'msgNr', value:nr}]);
(以上window[msg]([{name:'msgNr', value:nr}]);
)您还可以停止 Bean 处理并使用常用的 JQuery 回调按需运行它。为此创建一个函数并再次更改代码:
var jsonMsgNr = 0;
var jsonMessages = {};
var validmsg = { updateModel:1 }
var jsonMsgNrLast = 0;
var autoRun = true; // ADDED, set false control through GUI
if (window.WebSocket) {
var ws = new WebSocket("wss://localhost:8181/ContextPath/AdminPush");
ws.onmessage = function (event) {
var jsonMsg = event.data;
var json = JSON.parse(jsonMsg);
if (validmsg[msg] && window[msg]) {
var nr = ++jsonMsgNr;
jsonMessages[nr] = { jsonMsg:jsonMsg, json:json };
updateGuiPushList(nr, 1);
if (autoRun && !jsonMsgNrLast) {
runRemote(nr);
}
}
}
}
function realNotifyAll(nr) {
if (!(nr in jsonMessages)) return;
var jsonMsg = jsonMessages[nr].jsonMsg;
var json = jsonMessages[nr].json;
delete jsonMessages[nr]; // free memory
updateGuiPushList(nr, 0);
jsonMsgNrLast = 0;
if (autoRun)
runRemote(nr+1);
// Moved, but now must not rely on jsonMsgNrLast:
sendMessage(jsonMsg);
dosomething(json);
}
function runRemote(nr) {
if (nr==jsonMsgNrLast) return;
if (nr in jsonMessages)
{
if (jsonMsgNrLast) { alert("Whoopsie! Please wait until processing finished"); return; }
jsonMsgNrLast = nr;
updateGuiPushList(nr, 2);
// Be sure you are async here!
window[jsonMessages[nr].json.msg]([{name:'msgNr', value:nr}]);
}
}
现在您可以开始处理runRemote(nr)
并调用完成函数realNotifyAll(nr)
.
功能updateGuiPushList(nr, state)
with state=0:finished 1=added 2=running
是对 GUI 代码的回调,它更新屏幕上等待处理的推送列表。放autoRun=false
停止自动处理和autoRun=true
用于自动处理。
注:设置后autoRun
from false
to true
你需要触发runRemote
一次最低的nr
, 当然。