07 CORDIS / EVENT DISPATCH

监听器执行了,调用方等到了吗?

切换同一组演示输入,沿时间向下读。关注两件事:后续监听器是否运行,调用方何时取得结果。

01 / 派发方法

02 / 输入

前四种:A 返回 Promise(放行后为 undefined),B 返回 0,C 返回 last。
waterfall:独立的 next 包装组;终点为 base,返回时依次追加 C、B、A。异常输入均在 A 进入时直接抛错。

emit正常结果

A 尚未完成,派发已返回

emit 会调用 B、C,但不收集 A 的 Promise。外层 await emit 也无法补齐这份等待。

谁被调用
A、B、C
等待 / 停止
不等待 A
调用方结果
undefined
执行轨迹 / emit↓ 先后关系,非耗时比例
改用逐步轨迹查看返回时序图
  1. A 开始返回一份受控 Promise;尚未完成A:start
  2. B 执行同步返回 0B:0
  3. C 执行同步返回 'last'C:last
  4. 调用已返回结果为 undefined;不是 A 的 Promisecaller:undefined
  5. A 完成实验放行后,Promise 解析为 undefinedA:end
A 尚未完成,派发已返回emit 会调用 B、C,但不收集 A 的 Promise。外层 await emit 也无法补齐这份等待。。A:start;B:0;C:last;caller:undefined;A:end 派发端调用与收集 APromise → undefined B返回 0 C返回 last 调用 A Promise 调用 B 0 调用 C 'last' 派发返回 undefined 放行 A → 完成
调用返回 / 完成色块:派发端观察或跳过状态
  1. A 开始返回一份受控 Promise;尚未完成A:start
  2. B 执行同步返回 0B:0
  3. C 执行同步返回 'last'C:last
  4. 调用已返回结果为 undefined;不是 A 的 Promisecaller:undefined
  5. A 完成实验放行后,Promise 解析为 undefinedA:end
parallel正常结果

启动全部监听器,等 A 结束

B、C 的同步返回值不会成为派发结果。全部成功后,调用方等待到 undefined。

谁被调用
A、B、C
等待 / 停止
全部 settled
调用方结果
Promise<void>
执行轨迹 / parallel↓ 先后关系,非耗时比例
改用逐步轨迹查看返回时序图
  1. A 开始返回一份受控 Promise;尚未完成A:start
  2. B 执行同步返回 0B:0
  3. C 执行同步返回 'last'C:last
  4. 调用已返回 Promise此刻仅取得对象;是否继续由当前派发规则决定caller:Promise
  5. A 完成实验放行后,Promise 解析为 undefinedA:end
  6. 等待结束调用方 await 得到 undefinedcaller:resolved:undefined
启动全部监听器,等 A 结束B、C 的同步返回值不会成为派发结果。全部成功后,调用方等待到 undefined。。A:start;B:0;C:last;caller:Promise;A:end;caller:resolved:undefined 派发端调用与收集 APromise → undefined B返回 0 C返回 last 调用 A Promise 调用 B 0 调用 C 'last' 返回待定 Promise A 完成 派发解析为 undefined
调用返回 / 完成色块:派发端观察或跳过状态
  1. A 开始返回一份受控 Promise;尚未完成A:start
  2. B 执行同步返回 0B:0
  3. C 执行同步返回 'last'C:last
  4. 调用已返回 Promise此刻仅取得对象;是否继续由当前派发规则决定caller:Promise
  5. A 完成实验放行后,Promise 解析为 undefinedA:end
  6. 等待结束调用方 await 得到 undefinedcaller:resolved:undefined
serial正常结果

A 完成后才进入 B,0 会截断

A 最终返回 undefined,因此继续。B 的 0 满足 isBailed;C 没有执行机会。

谁被调用
A、B;跳过 C
等待 / 停止
逐个 await
调用方结果
Promise → 0
执行轨迹 / serial↓ 先后关系,非耗时比例
改用逐步轨迹查看返回时序图
  1. A 开始返回一份受控 Promise;尚未完成A:start
  2. 调用已返回 Promise此刻仅取得对象;是否继续由当前派发规则决定caller:Promise
  3. A 完成实验放行后,Promise 解析为 undefinedA:end
  4. B 执行同步返回 0B:0
  5. 等待结束调用方 await 得到 0;C 未执行caller:resolved:0
A 完成后才进入 B,0 会截断A 最终返回 undefined,因此继续。B 的 0 满足 isBailed;C 没有执行机会。。A:start;caller:Promise;A:end;B:0;caller:resolved:0 派发端调用与收集 APromise → undefined B返回 0 C返回 last 调用 A Promise 返回待定 Promise A 完成 调用 B 0 派发解析为 0;C 跳过
调用返回 / 完成色块:派发端观察或跳过状态
  1. A 开始返回一份受控 Promise;尚未完成A:start
  2. 调用已返回 Promise此刻仅取得对象;是否继续由当前派发规则决定caller:Promise
  3. A 完成实验放行后,Promise 解析为 undefinedA:end
  4. B 执行同步返回 0B:0
  5. 等待结束调用方 await 得到 0;C 未执行caller:resolved:0
bail正常结果

A 的 Promise 本身就是停止值

派发立即选中 A 返回的对象。它后来解析成 undefined,也不会让 B、C 重新加入。

谁被调用
只有 A
等待 / 停止
不等再判断
调用方结果
A 的 Promise
执行轨迹 / bail↓ 先后关系,非耗时比例
改用逐步轨迹查看返回时序图
  1. A 开始返回一份受控 Promise;尚未完成A:start
  2. 调用已返回 Promise此刻仅取得对象;是否继续由当前派发规则决定caller:Promise
  3. A 完成实验放行后,Promise 解析为 undefinedA:end
  4. 等待结束调用方 await 得到 undefinedcaller:resolved:undefined
A 的 Promise 本身就是停止值派发立即选中 A 返回的对象。它后来解析成 undefined,也不会让 B、C 重新加入。。A:start;caller:Promise;A:end;caller:resolved:undefined 派发端调用与收集 APromise → undefined B返回 0 C返回 last 调用 A Promise 返回 A 的 Promise B、C 跳过 A 完成 外层 await 得到 undefined
调用返回 / 完成色块:派发端观察或跳过状态
  1. A 开始返回一份受控 Promise;尚未完成A:start
  2. 调用已返回 Promise此刻仅取得对象;是否继续由当前派发规则决定caller:Promise
  3. A 完成实验放行后,Promise 解析为 undefinedA:end
  4. 等待结束调用方 await 得到 undefinedcaller:resolved:undefined
waterfall正常结果

沿 next 向内调用,沿 return 向外返回

A、B、C 分别在 next 的结果后追加字母。终点返回 base,最外层拿到 baseCBA。

谁被调用
A、B、C、inner
等待 / 停止
本组为同步链
调用方结果
baseCBA
执行轨迹 / waterfall↓ 先后关系,非耗时比例
改用逐步轨迹查看返回时序图
  1. A 进入调用 next(),控制权向内传递A:enter
  2. B 进入调用 next(),控制权向内传递B:enter
  3. C 进入调用 next(),控制权向内传递C:enter
  4. 进入终点 inner返回 'base'inner:base
  5. C 返回拿到内层结果,在末尾追加 CC:exit
  6. B 返回拿到内层结果,在末尾追加 BB:exit
  7. A 返回拿到内层结果,在末尾追加 AA:exit
  8. 调用已返回结果为 'baseCBA'caller:baseCBA
沿 next 向内调用,沿 return 向外返回A、B、C 分别在 next 的结果后追加字母。终点返回 base,最外层拿到 baseCBA。。A:enter;B:enter;C:enter;inner:base;C:exit;B:exit;A:exit;caller:baseCBA 派发端调用与收集 Anext 包装 Bnext 包装 Cnext 包装 inner终点函数 调用 A next() next() next() base baseC baseCB baseCBA
调用返回 / 完成色块:派发端观察或跳过状态
  1. A 进入调用 next(),控制权向内传递A:enter
  2. B 进入调用 next(),控制权向内传递B:enter
  3. C 进入调用 next(),控制权向内传递C:enter
  4. 进入终点 inner返回 'base'inner:base
  5. C 返回拿到内层结果,在末尾追加 CC:exit
  6. B 返回拿到内层结果,在末尾追加 BB:exit
  7. A 返回拿到内层结果,在末尾追加 AA:exit
  8. 调用已返回结果为 'baseCBA'caller:baseCBA
emitA 同步抛错

A 同步抛错,B、C 不执行

异常从 emit 调用处同步传播。广播并没有为每个监听器提供独立的错误隔离。

谁被调用
只有 A
等待 / 停止
当场中断
调用方结果
throw Error
执行轨迹 / emit↓ 先后关系,非耗时比例
改用逐步轨迹查看返回时序图
  1. A 抛错直接 throw Error('A'),并非返回拒绝 PromiseA:throw
  2. 调用方取得 Error同步抛出或 Promise 拒绝,见本场景返回栏caller:Error:A
A 同步抛错,B、C 不执行异常从 emit 调用处同步传播。广播并没有为每个监听器提供独立的错误隔离。。A:throw;caller:Error:A 派发端调用与收集 A同步 throw B返回 0 C返回 last 调用 A throw A 调用同步抛错 B、C 跳过
调用返回 / 完成色块:派发端观察或跳过状态
  1. A 抛错直接 throw Error('A'),并非返回拒绝 PromiseA:throw
  2. 调用方取得 Error同步抛出或 Promise 拒绝,见本场景返回栏caller:Error:A
parallelA 同步抛错

A 抛错后,B、C 仍会启动

async 包装将 A 的同步异常变为拒绝结果。等待全部 settled 后,派发拒绝为 AggregateError。

谁被调用
A、B、C
等待 / 停止
全部 settled
调用方结果
拒绝 AggregateError
执行轨迹 / parallel↓ 先后关系,非耗时比例
改用逐步轨迹查看返回时序图
  1. A 抛错直接 throw Error('A'),并非返回拒绝 PromiseA:throw
  2. B 执行同步返回 0B:0
  3. C 执行同步返回 'last'C:last
  4. 调用已返回 Promise此刻仅取得对象;是否继续由当前派发规则决定caller:Promise
  5. 等待拒绝AggregateError.errors 含 A 的错误caller:AggregateError:A
A 抛错后,B、C 仍会启动async 包装将 A 的同步异常变为拒绝结果。等待全部 settled 后,派发拒绝为 AggregateError。。A:throw;B:0;C:last;caller:Promise;caller:AggregateError:A 派发端调用与收集 A同步 throw B返回 0 C返回 last 调用 A throw A 调用 B 0 调用 C 'last' 返回待定 Promise 全部 settled 后拒绝
调用返回 / 完成色块:派发端观察或跳过状态
  1. A 抛错直接 throw Error('A'),并非返回拒绝 PromiseA:throw
  2. B 执行同步返回 0B:0
  3. C 执行同步返回 'last'C:last
  4. 调用已返回 Promise此刻仅取得对象;是否继续由当前派发规则决定caller:Promise
  5. 等待拒绝AggregateError.errors 含 A 的错误caller:AggregateError:A
serialA 同步抛错

A 失败,派发 Promise 拒绝

A 的同步抛错使 serial 返回的 Promise 拒绝。后续监听器不会继续执行。

谁被调用
只有 A
等待 / 停止
首个失败即停
调用方结果
拒绝 Error
执行轨迹 / serial↓ 先后关系,非耗时比例
改用逐步轨迹查看返回时序图
  1. A 抛错直接 throw Error('A'),并非返回拒绝 PromiseA:throw
  2. 调用已返回 Promise此刻仅取得对象;是否继续由当前派发规则决定caller:Promise
  3. 调用方取得 Error同步抛出或 Promise 拒绝,见本场景返回栏caller:Error:A
A 失败,派发 Promise 拒绝A 的同步抛错使 serial 返回的 Promise 拒绝。后续监听器不会继续执行。。A:throw;caller:Promise;caller:Error:A 派发端调用与收集 A同步 throw B返回 0 C返回 last 调用 A throw A 派发 Promise 拒绝 B、C 跳过
调用返回 / 完成色块:派发端观察或跳过状态
  1. A 抛错直接 throw Error('A'),并非返回拒绝 PromiseA:throw
  2. 调用已返回 Promise此刻仅取得对象;是否继续由当前派发规则决定caller:Promise
  3. 调用方取得 Error同步抛出或 Promise 拒绝,见本场景返回栏caller:Error:A
bailA 同步抛错

同步异常直接传给调用方

bail 没有 async 包装。A 抛错时,调用本身就抛错,B、C 不会被调用。

谁被调用
只有 A
等待 / 停止
当场中断
调用方结果
throw Error
执行轨迹 / bail↓ 先后关系,非耗时比例
改用逐步轨迹查看返回时序图
  1. A 抛错直接 throw Error('A'),并非返回拒绝 PromiseA:throw
  2. 调用方取得 Error同步抛出或 Promise 拒绝,见本场景返回栏caller:Error:A
同步异常直接传给调用方bail 没有 async 包装。A 抛错时,调用本身就抛错,B、C 不会被调用。。A:throw;caller:Error:A 派发端调用与收集 A同步 throw B返回 0 C返回 last 调用 A throw A 调用同步抛错 B、C 跳过
调用返回 / 完成色块:派发端观察或跳过状态
  1. A 抛错直接 throw Error('A'),并非返回拒绝 PromiseA:throw
  2. 调用方取得 Error同步抛出或 Promise 拒绝,见本场景返回栏caller:Error:A
waterfallA 同步抛错

A 没调用 next,链条止于 A

本场景在进入 A 时直接抛错,B、C 与 inner 均未运行。异常沿当前函数调用链向外传播。

谁被调用
只有 A
等待 / 停止
未进入 next
调用方结果
throw Error
执行轨迹 / waterfall↓ 先后关系,非耗时比例
改用逐步轨迹查看返回时序图
  1. A 抛错直接 throw Error('A'),并非返回拒绝 PromiseA:throw
  2. 调用方取得 Error同步抛出或 Promise 拒绝,见本场景返回栏caller:Error:A
A 没调用 next,链条止于 A本场景在进入 A 时直接抛错,B、C 与 inner 均未运行。异常沿当前函数调用链向外传播。。A:throw;caller:Error:A 派发端调用与收集 A同步 throw Bnext 包装 Cnext 包装 inner终点函数 调用 A throw A 调用同步抛错 B、C、inner 跳过
调用返回 / 完成色块:派发端观察或跳过状态
  1. A 抛错直接 throw Error('A'),并非返回拒绝 PromiseA:throw
  2. 调用方取得 Error同步抛出或 Promise 拒绝,见本场景返回栏caller:Error:A