← 返回目录

第 3 卷

第 3 / 4 卷日期:

6.map(hook => hook.callback.bind(thisArg))// 转成可调用的回调数组 7}

dispatch() 只做一件事:根据事件名,从 _hooks 里取出监听器列表,过滤后返回。五种模式的第一步都是调用它,拿到同一个列表。

真正的差异,全在「拿到列表之后怎么遍历」。看五种模式的实现,它们各自只有一行核心(node_modules/@deepseek-ai/cordis/src/events.ts):

复制代码 1// ① emit:同步 map,逐个调用,不管返回值

2emit(...args) { this.dispatch('emit', args).map(cb => cb(...args)) } 3

4//②parallel:并发map,等所有结果(有错就汇总成AggregateError)
5asyncparallel(...args) {*
6constresults =awaitPromise.allSettled(this.dispatch('emit', args).map(async
7...
8}

9

10//③serial:顺序for,逐个await,一旦isBailed就停下并返回
11asyncserial(...args) {
12for(constcbofthis.dispatch('serial', args)) {
13constresult =awaitcb(...args)
14if(isBailed(result))returnresult
15}
16}

17

18//④bail:serial的同步版,同样的for + isBailed,只是不await
19bail(...args) {
20for(constcbofthis.dispatch('bail', args)) {
21constresult =cb(...args)
22if(isBailed(result))returnresult
23}
24}
26//⑤waterfall:shift递归,每层拿next往下钻,形成洋葱
27waterfall(...args) {

把五种实现摆在一起,一个深刻的统一就浮现了:

「拿到监听器列表」这件事,五种模式完全一样——都是 dispatch();

「遍历列表」的策略不同,才造就了五种语义:

emit = 同步 map(不管结果)

parallel = Promise.allSettled(并发等待)

serial / bail = for + isBailed 短路(一个 await、一个不 await)

waterfall = shift 递归(类似多层的洋葱)

顺便提一句:serial/bail 里的 isBailed(result),就是之前说的那个判定—— value !== null && value !== false && value !== undefined。返回非空值

就「返回」,正是靠这一行。

模式回顾

五种模式都讲完了,我们回头用一张表 + 一张图,把它们整体收拢一下:

图 9-1
图 9-1

下次写事件时,你沿着这张快速决策图逐层问自己三个问题,答案自然就出来了:

复制代码 1要不要拿返回值?

2├── 不要 → 要不要等它们做完?

3│├── 不要 → emit(同步广播)

4│└── 要→ parallel(并发等待)

5└── 要 → 什么语义?

6├── "谁能处理谁接单"(竞争)→ 同步用 bail / 异步用 serial 7└── "层层加工/可短路"(环绕中间件)→ waterfall

本讲小结

今天这一讲,我们补齐了 Cordis 世界观的第三块通信拼图。

我们学会了五种分发模式:emit 同步广播、parallel 并发等待、serial 异步竞争、 bail 同步竞争、waterfall 洋葱式中间件。它们按“是否等待、是否并发、有无返回值”等

多个维度各司其职。

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