Fast Messenger 能帮你的 JavaScript object 和 function 发送 message,不论是在主 UI 线程中,web worker 中,甚至是跨越 UI 和 webworker 线程。这样的话,你的 JavaScript 程序立即自动地编成了多线程的了。
在这里我会用到一些代码,你可以在上一篇博文中找到完整的范例代码,或者直接下载本 project 的 zip。
首先我们来看看单例模式。你可以创建一个 Messenger 的单例,把你能接受的 message 的 object 注册好,然后就可以向他们发 message 了。
1 // Create a new instance of Fast Messenger 2 messenger = new Messenger(); 3 ...... 4 // Register your objects. In this case the object controls a html textarea. 5 messenger.registerReceiver("left:add", leftArea, leftArea.append); 6 messenger.registerReceiver("left:clear", leftArea, leftArea.clear); 7 ...... 8 // now you can send them messages like below from anywhere at anytime 9 messenger.sendMessage("left:add", "this is the first line"); 10 messenger.sendMessage("left:add", "the second line"); 11 messenger.sendMessage("left:clear");
Fast Messenger 提供的命名系统把 sender 和 receiver 分开了,messenger 总能为你的 message 找到 receiver,不论这个 receiver 在哪里,也不论是不是你写的。只要你知道它的名字,只要都在同一个 JavaScript 的内存里,messenger 总能找到它。在 UI application 里这个功能特别有用,因为 UI 里有很多的控件,由不同的人、甚至不同的公司写的,如果光知道一个名字就能发 message 给它的话,那真是太方便了。
你的程序大了的话,也可以创建多个 Messenger 的实例,从而将大量的 object 划分到更小的区域里去。
这个 Messenger() 可以在主 UI 里用,也可以在 web worker 里用,使用起来完全没有区别。但是一个实例不能同时在两个线程里一起使用,那种情况要用下面的设置来解决。
一边一个实例,通过 RemotePeer() 来连接。除了 RemotePeer() 那行外,代码是一模一样。
1 // Create a new instance of Fast Messenger 2 messengerA = new Messenger(); 3 ...... 4 // Register your objects. In this case the object controls a html textarea. 5 messengerA.registerReceiver("left:add", leftArea, leftArea.append); 6 messengerA.registerReceiver("left:clear", leftArea, leftArea.clear); 7 ...... 8 // Once you have registered your objects, wire the connector 9 // your messengerB is in the another.js file 10 worker = new Worker("another.js"); 11 peerAdapter1 = new RemotePeer(worker, messengerA); 12 ...... 13 // a happy sender 14 messenger.sendMessage("A1", "this is for receiver A1 in the UI"); 15 messenger.sendMessage("B1", "this is for receiver B1 in the web worker");
这是一边的代码,另一边也是类似。值得一提的要注意的地方是,两个实例共享一个命名空间。在不同边有同名的 receiver 出现是有可能的,但是这个 receiver 只能收到本地的 message。因为 messenger 总是找到一个 receiver 就停止了,这样本地的就总是优先了。
其实 Fast Messenger 不止能支持一个 web worker,它能同时支持多个 web worker。
这回光一个 RemotePeer() 不管用了,所以又加了一个 MessengerHub() 放在 UI 那边负责 message 在 UI 及各 web worker 之间的中转。一般来说一个 messenger 总是先找本地的 receiver,找不到了,就提交给 Hub。而 Hub 呢,先找本地的,就是 UI 中的 messenger,如果来的 message 是从某个 web worker 来的话。然后逐个查在 web worker 中的 messenger,看看它们存在 Hub 中的 receiver 的名单有没有匹配的,有的话就转发过去。Hub 也是使用一个 receiver 政策,找到一个就不再费劲了。
这种设置要多写一两行的代码来初始,但是应用和业务的代码不需要任何变化,因为本来 receiver 的位置对它们来说就是透明的,就是本来就不知道。
1 messenger1 = new Messenger(); 2 3 leftArea = new AreaController("leftText"); 4 messenger1.registerReceiver("left:add", leftArea, leftArea.append); 5 messenger1.registerReceiver("left:clear", leftArea, leftArea.clear); 6 7 hub = new MessengerHub(); 8 hub.setLocalPeer(messenger1); 9 10 worker1 = new Worker("MultiFmWorker1.js"); 11 peerAdapter1 = new RemotePeer(worker1, hub.newInnerPeer()); 12 13 worker2 = new Worker("MultiFmWorker2.js"); 14 peerAdapter2 = new RemotePeer(worker2, hub.newInnerPeer());
现在的这个设计是用 UI 线程为所有的 web worker 来中转 message,有可能会脱慢 UI 对用户的反应。当然,这只是潜在的问题,要看具体的应用。我是 JavaScript 的新手,正在学习中,如果有人知道如何在 web worker 之间发送 message 的话,请不吝赐教。