我有一个显示实时信息的Intranet Web系统。有服务器端进程将各种更新记录到数据库中。然后,各种网页都使用Ajax每隔几秒钟询问新内容。这都是标准的东西(想想RSS ...),到目前为止还可以。
但是...如果用户显示多个页面,这将导致多个Ajax请求-每个请求实际上都要求相同的数据。这对一些用户没有害处,但扩展性不好。因此,我希望每个用户有一个请求。有很多选项:
关于不同选择的相对收益/痛苦,您有什么想法吗?
这听起来像SharedWorker的工作。
您所有的页面都将订阅它,并等待它传输资源:
下面是live fiddle演示中的代码(不允许从StackSnippet生成null的帧来运行SharedWorkers。
const script_content = document.getElementById('worker-script').textContent;
const script_url = URL.createObjectURL( new Blob( [ script_content ] ) );
// This should be the only one per page
const worker1 = new SharedWorker( script_url );
worker1.port.onmessage = (e) => console.log('worker1 received data', e.data);
// This would be an other page
const worker2 = new SharedWorker( script_url );
worker2.port.onmessage = (e) => console.log('worker2 received data', e.data);
<script id="worker-script" >
const ports = []; // we'll store all the connected ports here
onconnect = e => { / everytime a process connects
ports.push( e.ports[ 0 ] );
};
setInterval( () => {
const rand = Math.random(); // fetched data
// transmit it to all connected processes
ports.forEach( port => port.postMessage( rand ) );
}, 1000
);
</script>
SharedWorker的最大问题是浏览器支持。您将无法在Safari上使用它。
但是我们现在有了the BroadcastChannel
API,它允许在同一域上的不同进程之间进行通信,并且由于存储事件而可以很容易地进行多文件归档。
因此,您可以尝试建立一个与SharedWorker相同的系统,但是要稍微复杂一些。
当浏览器连接到您的页面之一时,它将尝试加入BroadcastChannel或创建它。
它首先通过BroadcastChannel发送一条消息,询问是否有人连接。
如果没有人连接,则此过程将负责获取。
BroadcastChannel
中,它告诉连接的进程选择一个作为新的访存程序。否则
[onbeforeunload
,因为虽然允许StackSnippet奇怪地运行BroadcastChannel,但它们的null起源将使它们无法与任何实例共享...在几个选项卡中打开此链接,并查看他们如何相互交谈。
这里仍然是小提琴的代码:
Here is a jsfiddle proof of concept
const my_id = Math.random();
let I_am_the_master = false;
let connections = ["me"];
const master_timeout = ImDaMasta();
const channel = new BroadcastChannel('demo');
channel.onmessage = e => {
console.log(e.data);
switch (e.data.type) {
case "newData":
onnewdatareceived(e.data.data);
break;
case "disconnect":
ondisconnect(e);
break;
case "connect":
onconnect(e);
break;
case my_id:
oninitresponse(e);
}
};
channel.postMessage({
type: 'connect',
_id: my_id
});
onbeforeunload = e => {
channel.postMessage({
type: 'disconnect',
index: connections.indexOf('me')
});
}
// set ourselves as the master process
function ImDaMasta() {
I_am_the_master = true;
return setInterval(() => {
const rand = Math.random();
channel.postMessage({
type: "newData",
data: rand
});
log("I'm the master and did post", rand);
}, 1000);
}
// when we receive new data from the master
function onnewdatareceived(data) {
log("I'm a slave and received", data);
}
// when an other process disconnected
function ondisconnect(e) {
connections.slice(e.data.index, 1);
if (connections[0] === "me") {
ImDaMasta();
}
}
// when an other process connects
function onconnect(e) {
connections.push('other');
if (I_am_the_master) {
channel.postMessage({
type: e.data._id,
connections
});
}
}
// when the master process handled our connection
function oninitresponse(e) {
I_am_the_master = false;
clearTimeout(master_timeout);
connections = e.data.connections;
connections[connections.length - 1] = "me";
}
// helper
function log(...args) {
_log.textContent = args.map(o => JSON.stringify(o)).join(' - ');
}