文档教程事件处理

Loro 的事件处理

Loro 内置事件系统用于追踪文档变化。本节介绍事件的触发时机以及事务在 Loro 中的工作方式。

事件触发时机

只要内部文档状态发生变化,就会触发事件,帮助应用层的派生状态自动与文档同步。

  1. 本地操作:对于本地插入、删除等操作,Loro 会先将它们置于内部事务的 pending 状态。

  2. 事务提交:事务提交时,所有待处理操作会一次性触发相应事件。提交发生在以下场景:

    • 显式调用 LoroDoc.commit()
    • 在导入或导出前自动提交

    事件会在微任务结束后异步触发。如果需要在提交后立即处理事件,请等待一个微任务:

const  = new ();
.(() => {
  .("Event:", );
});
 
const  = .("text");
.(0, "Hello");
.();
// 事件尚未触发
await .();
// 事件现已触发
  1. 导入:调用 import() 导入远端更新时,会触发对应事件,便于本地响应其他节点的变更:
const  = new ();
.(() => {
  .("Event:", );
});
 
.(); // 微任务后触发事件
await .();
  1. 版本切换:调用 doc.checkout(frontiers) 切换到其他版本时,同样会在微任务后触发事件。
const  = new ();
const  = .();
.(); // 微任务后触发事件
await .();

事务行为

Loro 的事务主要用于将相关操作打包,并以整体事件的形式发出,常见用途包括:

  1. 关联操作:当一系列本地操作在逻辑上相关时,你可能希望它们:

    • 共享提交信息
    • 使用相同时间戳
    • 在撤销 / 重做时作为整体处理
  2. 事件处理:应用通常希望一次性收到相关变更,事务提供了:

    • 在提交时设置 origin 标识
    • 在事件中携带 origin 便于过滤

触发提交的方式

  1. 显式提交:直接调用 commit()

    const  = new ();
    const  = .("myText");
    .(0, "Hello, Loro!");
    .(); // 提交待处理操作并触发事件
  2. 导入 / 导出前:在执行导入或导出前会自动提交。

    const  = new ();
    .(1);
    const  = new ();
    .(2);
     
    // 对 doc1 与 doc2 进行一些操作
    .("text").(0, "Alice");
    .("text").(0, "Hello, Loro!");
    .(.().()); // Map(0) {}
    .(.().()); // Map(0) {}
    const  = .({ : "snapshot" });
    .(); // doc2 会先提交自身待处理操作
    .(.().()); // Map(2) { "1" => 5, "2" => 12 }
    .(.().()); // Map(2) { "1" => 5 }

Loro 中的事务

需要强调的是,Loro 的事务不同于传统数据库事务:

  • 不具备 ACID 属性
  • 主要充当事件包装器
  • 操作失败时不会自动回滚