← 返回目录

第 1 卷

第 1 / 4 卷日期:

张嘉熙 · DeepSeek Harness 前沿工程实践

图 1-1
图 1-1
图 1-2
图 1-2
图 1-3
图 1-3

你好,我是张嘉熙。

前两讲我们打通了时间(可撤销 Effect)和空间(响应式依赖)两个维度。这一讲补上第三块拼图——通信。前面讲的依赖解决的是“要”(A 需要 B 的能力)的问题,但插件之间还有一

种关系是“说”(A 要通知 B 发生了某件事)。比如用户发出一个事件,日志插件要记录、统计插件要计数——事件发出方和事件接收方没有直接依赖关系,只是这些插件都想“听到”这件

事。

Cordis 中通信的核心在于它的事件系统,它把事件分发这件事,拆成了五种模式。

我们先看看 Cordis 中事件系统的基本使用方式:

复制代码 1// 来自官方 cordis-tutorial 第四章的 stats.ts(节选)

7

8exportclassStatsServiceextendsService{
9bump(name:string) {
10// ...*
11this.ctx.emit('stats/report', name, next) //发出事件,此处的next为当前count自增
12}
13}

复制代码

1//来自官方cordis-tutorial第四章的reporter.ts(节选)
2exportfunctionapply(ctx: Context) {
3ctx.on('stats/report',(name, count) =>{ //监听事件
4console.log(`[stats]${name}->${count}`)
5})
6}

这里我们已经能看到事件系统的两个核心动作,以及一种似曾相识的声明方式:

  1. emit(发出)和 on(监听):一方 emit('stats/report', ...) 发出,另一方

on('stats/report', ...) 监听。事件名用 namespace/action 的斜杠形式,因为所有事件共享一个扁平命名空间,斜杠前缀(stats/)能把相关事件分组。

  1. declare module { interface Events } 这和上一讲的声明合并很类似——它把

「事件名 + 监听器签名」写进声明,让 emit/on 都获得类型检查(写错编译期就报错)。

下面我们直接跑起来,逐个拆解这五种分发模式。

emit 与 parallel——通知与通知并等待

这两个模式都用于广播,你不想从监听器那里拿回什么,只是想告诉它们事情发生了。区别在于要不要等它们做完。

3})

4ctx.on('chat/message', (text) => {

5console.log('[统计插件]消息字数:', text.length)
6})
7ctx.emit('chat/message','你好,Cordis')

emit 是同步的:它按注册顺序逐个调用监听器,不等待(即使监听器返回 Promise 也不管),不收集返回值。所以,`emit` 适合“发通知”这类 fire-and-forget 的场景。

顺便提一下:除了 ctx.on,Cordis 还有 ctx.once——监听器在首次被触发后自动注销,适合只关心下一次事件的场景,比如等连接建立就执行一次初始化。两者都返回一个

disposer,且都随插件卸载自动移除。

parallel:并发执行,一起等待

复制代码 1ctx.on('task/run', async (name) => {

2await new Promise(r => setTimeout(r, 100))// 模拟耗时任务 A*console.log('[任务 3})

4ctx.on('task/run', async (name) => { 5await new Promise(r => setTimeout(r, 100))// 模拟耗时任务 B*console.log('[任务

6})

7await ctx.parallel('task/run', '并行任务')

parallel 会 await 所有监听器,而且它们是并发跑的。当你有多个互不依赖的耗时任务(比如同时调用多个外部 API),用 parallel 能省下大量时间。注意:如果任何一个监听

器抛错,parallel 会抛出一个 AggregateError 汇总所有错误。

一句话区分:emit 是我通知你们,但我没空等;parallel 是我通知你们,并且等你们都做完。

全站分卷索引 · 4 卷
  1. 1第 1 卷
  2. 2第 2 卷
  3. 3第 3 卷
  4. 4第 4 卷