在实时显示数据的web程序中,需要不断将服务端数据送到客户端显示,在客户端获取数据的方式上,web程序的实现方法经历了从“不断请求“到“long polling“再到websockets的历程。
long polling
最初的做法是,客户端浏览器使用 setInterval 或者 setTimeout 来不断Ajax请求后端。
setInterval(function () {
$.ajax({url: "server"}).then(function (data) {
// show some thing with data
}
}, 3000)
使用setInterval有个缺陷,如果在setInterval指定的时间内比如上面的3秒内,服务端没有返回,那么其对应的那次请求就丢了数据。还有一个缺陷是,不同的ajax返回顺序不一定按照请求顺序返回。
可以使用递归setTimeout来解决上面两个问题。
(function poll() {
setTimeout(function () {
// ajax
$.ajax({url: 'server'}).then(function(data) { // show data poll() // 递归 })
}, 3000)
})()
这种不断请求的方法肯定是很耗带宽的。可以考虑使用long polling来优化。long polling没有做什么魔法的事情,就是服务端做了一点点的改变。这一点点改变就是“由原来的立即返回,改为延时返回“。也就是说,请客户端请求过来后,服务端原本不管有没有数据,都会返回该请求的,现在变为“等到有数据后“返回。这就意味着客户端的一次请求肯定是有数据的,从而充分利用了每一次客户端的请求。
var http = require("http");
var requests = [];
http.createServer(function(request, response) {
// 先把请求放进数组里暂存起来
requests.push({
response: response,
timestamp: new Date().getTime()
});
}).listen(8000);
setInterval(function() {
var expiration = new Date().getTime() - 5000;
var data = getData();
for (var i = requests.length - 1; i >= 0; i--) {
var response = requests[i].response;
if (requests[i].timestamp < expiration) { // 请求超过5秒的直接返回空
response.writeHead(200, { "Content-Type": "text/plain" });
response.end("");
} else {
if (data) { // 如果有数据则返回之,如果没有则等待下一个interval
response.writeHead(200, { "Content-Type": "text/plain" });
response.end(data);
}
}
}
}, 1000);
websockets
关于websockets,维基百科给了很棒的介绍
利用websockets,服务端可以主动推送数据到客户端。
使用起来也是无比简单。客户端,
var myWebSocket = new WebSocket("ws://www.websockets.org");
myWebSocket.onopen = function(evt) { alert("Connection open ..."); };
myWebSocket.onmessage = function(evt) { alert( "Received Message: " + evt.data); };
myWebSocket.onclose = function(evt) { alert("Connection closed."); };
myWebSocket.send("Hello WebSockets!");
myWebSocket.close();
服务端
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});