This commit is contained in:
呈祥
2026-01-06 17:31:56 +08:00
commit 210d948353
21 changed files with 4215 additions and 0 deletions
+83
View File
@@ -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);
// 应该有超时重发机制,这里没有实现
}
}