服务器发送事件

服务器法发送事件,HTML 5新特性,用于解决服务端更新问题,下面是简单实现:

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!-- 新建index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul id="list"></ul>
</body>

<script>

var list = document.getElementById('list');
function writePage(data) {
list.innerHTML += '<li>' + data + '</li>';
}

var eventSourceObj = new EventSource('http://localhost:3000/eventSource');
// 默认的类型
eventSourceObj.addEventListener('message', function(event) {
writePage('message : ' + event.data);
});
// 自定义类型
eventSourceObj.addEventListener('test', function(event) {
writePage('test : ' + event.data);
});
eventSourceObj.addEventListener('open', function(event) {
console.log('***********open***********');
});
eventSourceObj.addEventListener('error', function(event) {
alert(event);
});
</script>

</html>

服务端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
const Koa = require('koa');
const fs = require('fs');
const Readable = require('stream').Readable;
const app = new Koa();

const getFile = path => {
return new Promise((resolve, reject) => {
fs.exists(path, exists => {
if (!exists) reject(exists);
fs.readFile(path, (err, content) => {
if (err) reject(err);
resolve(content.toString());
});
});
});
};

class myReadStream extends Readable{
constructor(){
super();
}
_read(){}
}

const buildData = (event, data) => {
// 连接断开后,服务器默认3s重连一次,这里设置为10s
// id可用于区分每次连接,这里简单设置
return `id:1\nevent:${event}\nretry:10000\ndata:${data}\n\n`
}

const routers = {
'/': async ctx => {
ctx.type = 'text/html';
return await getFile(`${__dirname}/index.html`);
},
'/eventSource': async ctx => {
ctx.type = 'text/event-stream';
ctx.set('Cache-Control', 'no-cache');
ctx.set('Connection', 'keep-alive');

const stream = new myReadStream();
stream.push(buildData('message', Math.random()));
stream.push(buildData('test', Math.random()));
setInterval(() => {
stream.push(buildData('message', Math.random()));
stream.push(buildData('test', Math.random()));
}, 3000);

return stream;
}
};

app.use(async ctx => {
console.log(ctx.path);
ctx.body = routers[ctx.path] ? await routers[ctx.path](ctx) : 'Not Found';
});

app.listen('3000');

效果

参考