原文链接: 使用缓存和优先队列解决同步消息乱序的问题
上一篇: cocos 使用帧事件用原生动画包装spine动画
下一篇: cocos 图片放缩变化
小坑, 这个优先队列的toArray不是按照顺序, 应该是最小堆的遍历顺序, 这个应该是出于性能考虑, 所以需要自己转一下, 拿到真正的顺序
http://mauriciosantos.github.io/Buckets-JS/symbols/buckets.PriorityQueue.html
sender 发送同步消息到server
server 接收到sender的消息, 然后将消息发送到receiver
receiver 接受消息执行动作, 实现和sender的同步
问题:
在拖拽等触发间隔很短的情况下, 虽然可以扩大间隔, 使用线性插帧来实现同步, 但是在sender路径是非线性的情况下还是会有一些视觉上的差异
sender发送到server这个中间因为网络原因, server接收的顺序可能不一定准确, 一般server是不会关心这些稍微偏向业务的东西, 只是透传消息... 所以脏活累活都得前端干 o(╥﹏╥)o
server发送给receiver也由于网络原因会导致收到的不一定是发送的顺序, 这也是server做缓存和保证顺序基本上没啥用的原因
所以缓存只能在receiver做
基本思想:
用一定的时延确保消息的顺序
在收到消息后, 等待一段时间, 基本上四倍最小发送间隔就可以了, 一般都是两个消息乱序, 不会太多, 不过这个时间也是可以调节的, 视具体情况而定
在等待的过程中, 如果收到了消息, 将其加到缓存队列中, 队列使用发送时间排序 ,这样每次真正发送的是队列中的最先发送的消息, 而不是最先到达的消息
模拟发送和接受的延迟, 可以看到, 经过网络延时, 收到的未必是按照发送的顺序
延时100ms 使用优先队列做缓存, 可以看到已经是正确的顺序了
应该还有优化空间, 比如可以稀释消息, 或者按照优先级来排序, 鼠标移动优先级应该比点击小
也可以使用rxjs优化代码
import { PriorityQueue } from "buckets-js"
import { sleep } from "../../utils"
export class Receiver {
constructor() {
this.dataList = []
this.isSyncing = false
this.queue = new PriorityQueue((a, b) => {
if (a.senderTime < b.senderTime) {
return 1
}
if (a.senderTime > b.senderTime) {
return -1
}
return 0
})
}
onReceive(data) {
data.receiveTime = +new Date()
this.dataList.push(data)
console.log("onReceive", data)
this.syncData(data)
// 这个输出的array不是按照顺序的...应该只是最小堆的遍历
// this.queue.toArray()
}
doAction(data) {
console.log("doAction", data)
}
// 收到数据执行同步
async syncData(data) {
this.queue.add(data)
if (!this.isSyncing) {
this.isSyncing = true
// 如果没有正在同步, 则开始同步
// 先等待100ms
await sleep(100)
// 队列只要不是空, 就一直执行
while (!this.queue.isEmpty()) {
await sleep(32)
const item = this.queue.dequeue()
this.doAction(item)
}
this.isSyncing = false
} else {
// 如果正在同步只加入队列
}
}
toArray() {
while (!this.queue.isEmpty()) {
const item = this.queue.dequeue()
console.log(item)
}
}
}