init
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
// 客户端实现
|
||||
import { SimpleTextOperation as TextOperation } from "./op";
|
||||
import { getDemoServerInstance } from "./server";
|
||||
|
||||
// 直接获取server实例,这里省略了网络过程
|
||||
const server = getDemoServerInstance();
|
||||
|
||||
export class Client {
|
||||
// 当前文档
|
||||
public document: string = "";
|
||||
// 当前服务端文档状态
|
||||
public syncedDoc: string = "";
|
||||
// 下一个期望的修订号
|
||||
public revision: number;
|
||||
// 正在处理的操作,已经发送给服务端等待确认
|
||||
public outstanding: TextOperation | null = null;
|
||||
// 本地缓存的待发送操作队列(包含outstanding)
|
||||
public buffer: TextOperation[] = [];
|
||||
|
||||
constructor() {
|
||||
// 模拟连接服务器,获取初始文档状态和修订版本号
|
||||
const { document, revision } = server.connect(this);
|
||||
this.revision = revision;
|
||||
this.document = document;
|
||||
}
|
||||
|
||||
// 当用户更改文档时调用此方法
|
||||
applyClient(operation: TextOperation): void {
|
||||
this.document = operation.apply(this.document);
|
||||
this.buffer.push(operation);
|
||||
this.sendOperationIfNeed();
|
||||
}
|
||||
|
||||
// 接收到来自服务器的新操作,可能是另一个客户端的操作,也可能是服务端确认本地操作的响应
|
||||
applyServer(revision: number, operation: TextOperation): void {
|
||||
if (revision < 0 || this.revision < revision) {
|
||||
throw new Error("operation revision not in history");
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.buffer.length; i++) {
|
||||
// TODO: check
|
||||
// 将buffer中的操作转换为基于服务端文档状态的操作
|
||||
this.buffer[i] = TextOperation.transform(this.buffer[i], this.buffer[i - 1] || operation)[0];
|
||||
}
|
||||
|
||||
// 本地重新应用buffer中的操作,合并其它客户端的操作到本地
|
||||
let newDoc = this.syncedDoc;
|
||||
for (let i = 0; i < this.buffer.length; i++) {
|
||||
newDoc = this.buffer[i].apply(newDoc);
|
||||
}
|
||||
this.document = newDoc;
|
||||
|
||||
// 更新本地同步状态
|
||||
this.syncedDoc = operation.apply(this.syncedDoc);
|
||||
this.revision = revision;
|
||||
|
||||
// 操作来自服务端确认本地操作的响应
|
||||
if (operation.id === this.outstanding?.id) {
|
||||
this.outstanding = null;
|
||||
this.serverAck();
|
||||
}
|
||||
}
|
||||
|
||||
// 服务端已确认收到发送的操作,从buffer中移除该操作,尝试发送下一个
|
||||
serverAck(): void {
|
||||
this.outstanding = null;
|
||||
this.buffer.shift();
|
||||
this.sendOperationIfNeed();
|
||||
}
|
||||
|
||||
// 逐个向服务端发送本地待提交的操作。发送后等待服务端确认,触发serverAck方法后继续发送下一个操作
|
||||
async sendOperationIfNeed() {
|
||||
if (this.outstanding || this.buffer.length === 0) {
|
||||
// 有正在发送的操作,或者没有待提交的操作,no-op
|
||||
return;
|
||||
}
|
||||
// 向服务端发送本地待提交的操作,这里没有实现操作压缩,我们一个一个发送
|
||||
this.outstanding = this.buffer[0];
|
||||
// 模拟向服务端发送操作,实际实现应该通过网络
|
||||
server.receiveOperation(this.revision, this.outstanding);
|
||||
// 应该有超时重发机制,这里没有实现
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user