由于项目中需要定时的请求后端接口获取数据实时更新显示在页面,当我们需要做轮询的时候,自然就想到了定时器,setInterval
可以重复执行,所以轮询的时候理所当然的选择了 setInterval
,刚开始效果杠杠滴,可是过了一段时间如果网络波动就会导致定时器不是那么 ‘准时’ ,当时很奇怪,后面查找资料发现这是 setInterval
的一个弊端,
setInterval 的作用是每隔一段指定时间执行一个函数,但是这个执行不是真的到了时间立即执行,它真正的作用是每隔一段时间将事件加入事件队列中去,只有当当前的执行栈为空的时候,才能去从事件队列中取出事件执行。所以可能会出现这样的情况,就是当前执行栈执行的时间很长,导致事件队列里边积累多个定时器加入的事件,当执行栈结束的时候,这些事件会短时间内连续触发,因此就不能到间隔一段时间执行的效果。并且可能会造成性能问题。这就是setIntervel
的累积效应
1、对自己调用的代码是否报错会无视掉。即自己调用的代码即使错误,也会继续执行下去
2、setInterval无视网络延迟
我们在向服务器轮询数据的时候,如果发生网络卡顿的情况,客户端收到请求响应的时间大于interval循环的时间。而setInterval会无视任何情况下继续定时执行,这就会导致了用户的客户端里充斥着客户端的请求。
3、如果你调用的函数需要花较长时间才能完成,那某些调用会被直接忽略。
解决方案:
使用setTimeout
代替setInterval
。
针对 setInterval 的这个缺点,我们可以使用 setTimeout 递归调用来模拟 setInterval,这样我们就确保了只有一个事件结束了,我们才会触发下一个定时器事件,这样解决了 setInterval 的问题。
var timer;
function func(args){
//函数本身的逻辑
...
//函数执行完后,重置定时器
timer = setTimeout(func, 100, args);
}
timer = setTimeout(func, 100, args);
// 异步请求接口情况
function poll() {
setTimeout(function() {
$.get("/path/to/server", function(data, status) {
console.log(data);
// 前端接收到后端返回的数据时发起下一次请求
poll();
});
}, 10000);
}复制代码
如果确实要保证事件“匀速”被触发,那可以用希望的延迟减去上次调用所花时间,然后将得到的差值作为延迟动态指定给setTimeout。但是实际上由于浏览器的一些机制(垃圾回收、JavaScript是单线程)也无法做到很精确的执行。此外,当前浏览器也会将最小的超时时间固定在4ms到15ms之间。因此不要指望一点误差也没有
ps:setTimeout 会出现 this 指向全局的问题,不过现在使用箭头函数就可以很方便避免这种问题了
由此需求,自己去查看定时器的一些坑的同时,又接着去了解了js的eventloot
事件循环机制,紧接着发现在vue
中的nextTick
也是运用了异步队列思想实现的
setTimeout应用:
setInterval应用:
tcp握手挥手
,因此建立Tcp连接是非常消耗资源。网络负载比较大。VUE中使用websocket通信
websocket简介:
WebSocket 连接本质上就是一个 TCP 连接,和http协议同属于应用层。浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据,实现了浏览器和客户端双工通信,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。只需要建立一次连接
websocket特点:
websocket规范:
为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息Upgrade: WebSocket
、Connection: Upgrade
表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息
客户端到服务端:
GET /demo HTTP/1.1
Host: example.com
Connection: Upgrade
Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
Upgrade: WebSocket
Sec-WebSocket-Key1: 4@1 46546xW%0l 1 5
Origin: http://example.com
[8-byte security key]
服务端到客户端:
HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://example.com
WebSocket-Location: ws://example.com/demo
[16-byte hash response]
vue中使用websocket:
<template>
<div>
</div>
</template>
<script>
export default {
data() {
return {
websock: null,
}
},
created(){
//页面刚进入时开启长连接
this.initWebSocket()
},
destroyed: function() {
//页面销毁时关闭长连接
this.websocketclose();
},
methods: {
//初始化weosocket
initWebSocket(){
const wsuri = process.env.WS_API + "/websocket/threadsocket";//ws地址
this.websock = new WebSocket(wsuri);
this.websocket.onopen = this.websocketonopen;
this.websocket.onerror = this.websocketonerror;
this.websock.onmessage = this.websocketonmessage;
this.websock.onclose = this.websocketclose;
},
websocketonopen() {
console.log("WebSocket连接成功");
},
websocketonerror(e) { //错误
console.log("WebSocket连接发生错误");
},
websocketonmessage(e){ //数据接收
const redata = JSON.parse(e.data);
console.log(redata.value);
},
websocketsend(agentData){//数据发送
this.websock.send(agentData);
},
websocketclose(e){ //关闭
console.log("connection closed (" + e.code + ")");
},
},
}
</script>
跨平台的WebSocket通信库socket.io:
跨平台的WebSocket通信库,具有前后端一致的API,可以触发和响应自定义的事件。socket.io最核心的两个api就是emit 和 on了 ,服务端和客户端都有这两个api。通过 emit 和 on可以实现服务器与客户端之间的双向通信。
《Web通信中传统轮询、长轮询和WebSocket简介》
客户端定时向服务器发送请求,服务器接到请求后马上返回响应信息并关闭连接。
浏览器只需启动一个HTTP请求,其连接的服务器会hold
住此次连接,直到有新消息才返回响应信息并关闭连接(或到了设定的超时时间关闭连接),客户端处理完响应信息后再向服务器发送新的http请求。
HTTP1.1通过使用Connection:keep-alive
进行长连接,HTTP 1.1默认进行持久连接。在一次 TCP 连接中可以完成多个 HTTP 请求,但是对每个请求仍然要单独发header
,Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。
websocket的长连接,是一个真的全双工的Tcp长连接
。长连接第一次tcp链路建立之后,后续数据可以双方都进行发送,不需要发送请求头header
《前端频繁请求接口》、《多个异步请求的执行顺序》
简单一些,定义一个全局Id
,每次请求(是一个闭包)前把全局Id自增(比如Id=setTimeout(0);
),然后存一个到当前闭包里,请求结束后,比较闭包里的Id和全局Id,一致则更新UI,否则就是该请求作废了
一开始回答节流,但是节流适合避免短时间内多次请求,但是并不能保证请求返回后按顺序更新UI