Skip to content

第 12 章:Owner、并发请求与 AllNetworks ​

1. scopeKey 如何构造 ​

buildNativeHomePortfolioScopeKey 把影响展示作用域的因素串起来:

ts
return [
  enabled ? 'enabled' : 'disabled',
  walletId ?? '',
  accountId ?? '',
  networkId ?? '',
  isAllNetworks ? 'all' : 'single',
].join('::');

如果这些值中任意一个改变,旧的异步 token 请求、快照 partition、slot owner 和 Home semantic state 都不应继续被视为同一个展示上下文。

2. epoch + generation:两个单调量 ​

量保护什么
owner epoch识别 scope 改变;所有旧 request token 都不再 current
request generation同一 owner 下保证后发/更新的 request 不被更旧结果覆盖
sessionIdnative slot/intent 的生命周期归属
command revision当前 UI 是否仍支持某个 command

isNativeHomePortfolioRequestCurrent 要求 current owner 的 epoch/scope 与 request 一致,并且 request generation 不低于当前 generation。

3. Single-flight runner ​

Home portfolio 可能同时收到初始化、刷新、polling 或 Store 重算事件。createNativeHomeSingleFlightRunner 以 epoch::scopeKey 为 key:

  1. 同 owner 已有 task 时,不开第二个并发请求;
  2. 把最新 task 替换进去,设置 rerunRequested;
  3. 当前 task 完成后,如果 owner 仍然 current,再执行一次 coalesced task;
  4. owner 已过期就丢弃 queued rerun。

这比简单 debounce 更适合网络请求:它不丢掉“需要再跑一次”的语义,也不允许陈旧 owner 在 completion 后继续发起工作。

4. AllNetworks 的 authoritative 判定 ​

AllNetworks 会并行请求多个网络,因此“部分成功”不一定能组成 authoritative view。isNativeHomeAllNetworkResponseAuthoritative 把 isSameAllNetworksAccountData !== false 视为可接受;最终 status 还要满足:

  • start 成功;
  • 非空账户时 expected request count > 0;
  • attempt/failure/success count 与 expected count 完全一致;
  • 空账户仅在 expected request count 为 0 且 start 成功时才是 success。

5. AllNetworks portfolio projection ​

buildNativeHomeAllNetworkPortfolioProjection 不直接 concat 数组,而是:

  1. 为每个 response 的 token 标记 mergeAssets;
  2. 通过 buildSelectorTokenListFromResponses 生成 regular/high value projection;
  3. 按 network 分组构建 risk projection;
  4. 使用 canonical $key 去重;
  5. 重新切出 tokens、smallBalanceTokens、riskTokens 及对应 map;
  6. 按 fiat value 排序。

测试特别验证了 derive/aggregate、risk merge、row-level merge marker 和 canonical identity,说明这里的“去重”不是简单按 symbol 或 address。

6. Owner 与 slots 的闭环 ​

同一个 owner 概念跨越四层:

text
portfolio scopeKey
  -> Home session ownerToken
  -> IHomeContainerState.owner
  -> IHomeContainerSlotBundle.owner
  -> native ownsSlot / ownsSlotWithScopeKey

如果只在 JS Store 里做 owner check,而不把 owner 传到 native slot,旧 React slot 仍可能覆盖新 native host;如果只在 native 做 check,而不在 Store 做 request/revision check,旧 intent 仍可能触发新业务。两边必须同时做。

7. 这套并发模型的边界 ​

它能防止“旧 owner 结果覆盖新 owner”,但不会让网络永远成功,也不会把 partial AllNetworks response 变成完整数据。失败仍然会进入 error/empty/loading semantic state,由 ViewModel 决定安全的视觉 fallback。

基于 app-monorepo 2026-07-31 源码快照整理 · 非官方文档