Skip to content

register-notifies-watchers 的「订阅者重读看得见新 body」用例,实际钉住的是 get() 里的 microtask 深度,不是它声称的顺序保证 #6043

Description

@baozhoutao

#5840 时被这条用例真实拦下一次,按 PD #10 单独记录。观察类:今天没有用户碰得到的行为缺陷,红的是测试的健壮性,不是产品。

事实

packages/metadata/src/register-notifies-watchers.test.ts

it('announces AFTER the write lands, so a subscriber that re-reads sees the new body', async () => {
  let readBack: unknown;
  manager.subscribe('object', async () => {
    readBack = await manager.get('object', 'account');
  });

  await manager.register('object', 'account', { name: 'account', label: 'Fresh' });

  expect(readBack).toEqual({ name: 'account', label: 'Fresh' });
});

notifyWatchersLocalvoid callback(event) 派发(metadata-manager.tsprivate notifyWatchersLocal 一带),从不 await 回调。所以 register() 返回时,订阅者那句 await manager.get(...) 还没结算;readBack 能在断言前被赋值,靠的是「get() 内部的 await 跳数」恰好少于 await manager.register(...) 让出的微任务数。

为什么这是个 finding

它声称钉的顺序保证(先写落地、后广播)是真的,也确实值得钉。但这个写法测不到它:真正决定成败的是 get() 里有几个 await 帧。

实测证据(#5840 开发过程中):把 get()

async get(type, name) { /* registry 命中直接 return */ ... const result = await this.load(type, name); ... }

改写成等价的 return (await this.getDiagnosed(type, name)).data; —— 语义逐字不变、registry 优先不变、返回值不变,只多了一个 async 帧 —— 这条用例即刻变红,readBackundefined#5840 因此放弃了那个(更整洁的)委托写法,保留三行重复以维持逐帧一致,并把原因写进了 get() 的 TSDoc。

也就是说:一次纯粹的等价重构会让它红,而它声称保护的顺序保证毫发无损。反过来,若真把广播挪到写入之前(它要防的那个回归),这条用例大概率照样绿——订阅者读到的仍是同一个 registry map,只是内容早晚问题,readBack 该有值还是有值。假阴性与假阳性都在。

可能的修法(未择,留给承接座位)

  • (a) 让订阅者把 promise 记下来,断言前 await 它——最小改动,测的仍是「重读看得见」,但不再依赖跳数;
  • (b) 直接钉真正的不变量:在回调里同步(manager as any).registry.get('object').get('account'),断言广播那一刻写已落地——这才是「AFTER the write lands」的字面意思;
  • (c) 顺带考虑 notifyWatchers 是否该给一个「已排空」的可观测点,供测试与集群桥接共用(范围更大,多半不值)。

倾向 (b):它把断言对准了方法名承诺的那件事,且对 get() 的实现细节完全免疫。

关联

#5840(触发点,PR 里 get() TSDoc 常驻记了这条)、#3112(这批用例的来源单)。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions