Worker、策略与重连
配置 Worker/SharedWorker client、限制权威 action,并安全处理 epoch 变化。
Dedicated Worker
Dedicated Worker 为一个页面提供独立的权威生命周期:
const worker = new Worker(new URL('./worker.ts', import.meta.url), {
type: 'module'
});
const store = create(source, { worker });当 client 不再拥有连接时销毁 Coaction store;根据应用所有权模型决定何时终止 Worker。
跨标签页 SharedWorker
一个可复用模块可在页面 runtime 中创建 SharedWorker,并在 Worker runtime 内成为权威端:
import { create } from 'coaction/shared';
let worker: SharedWorker | undefined;
if (typeof SharedWorker !== 'undefined') {
try {
worker = new SharedWorker(new URL('./store.ts', import.meta.url), {
type: 'module'
});
} catch (error) {
console.error('SharedWorker construction failed', error);
// 只有在允许各标签页持有独立 local state 时,才保留 undefined。
}
}
export const store = create(source, { worker });每个标签页得到一个 client mirror,SharedWorker 拥有唯一状态实例。显式传入值为 undefined 的
worker key 时,store 会降级为 local authority。其 getState() action 仍返回 Promise,state、
action 参数与返回值也继续遵守 shared JSON contract,因此 await 后的调用点可以在两种模式间迁移。
Fallback 不是 client mirror
降级后的标签页各自持有独立状态。如果要求所有标签页共享同一状态,请把 SharedWorker 缺失当作
硬错误处理,而不是依赖降级。降级 store 的 share === false,并在本地拥有权威状态,因此底层
setState() 与 apply() 仍然可用。请始终通过 getState() 调用 action,并在读取结果前 await。
如果 new SharedWorker(...) 在调用 create() 之前就抛出异常,Coaction 无法捕获。只有当产品
确实允许 local fallback 时,应用才应自行捕获构造失败并保留 undefined。已经存在但在初始化
期间失败的 worker 或 transport 会显式报错,不会静默改变 authority model。
限制权威端 surface
权威端只暴露从 store 中发现的方法路径,transportPolicy 可进一步收紧:
const authority = create(source, {
transport,
transportPolicy: {
allowedActions: [['counter', 'increment']],
authorize(request) {
return request.type === 'fullSync' || hasAccess(request);
},
mapError(error) {
return error instanceof DomainError ? error.publicMessage : undefined;
}
}
});allowedActions是方法路径 allowlist;authorize接收已解码 JSON execute 或 full-sync request;mapError可以返回刻意向 client 公开的消息。
未预期的 action error 会被脱敏为 Remote action failed,原始错误留在权威端。Policy code 是可信应用代码,应尽量保持无副作用。
Epoch 与 sequence 恢复
每个权威生命周期拥有唯一 epoch,sequence 从 0 开始。client 会:
- 获取原子
{ epoch, sequence, state }snapshot; - 只应用该 epoch 的下一条 sequence;
- 忽略 duplicate 或更旧的 update;
- 发现 sequence gap 或 epoch 变化后请求 full snapshot。
Snapshot replacement 是原子的;validation 或 apply 失败时,client 保留此前的 epoch、sequence 与 state。
等待 action 对镜像可见
Action response 包含权威 epoch 与结果 sequence。client 在 resolve Promise 前等待镜像追上该位置;若 incremental update 在 executeSyncTimeoutMs(默认 1500 ms)内未到达,则回退到 full sync。
const store = create(source, {
worker,
executeSyncTimeoutMs: 3000
});只有当合法的 Worker 执行和消息调度经常超过默认值时才提高 timeout。它不能替代对损坏 transport 的修复。
Action 执行期间权威端变化
如果 action 在途时权威端发生变化,来自旧 epoch 的 response 会以 ActionAuthorityChangedError 拒绝。
其 outcome 为 unknown:旧权威端可能已经执行 action。只有 action 幂等或受应用级 idempotency key 保护时才重试。
import { ActionAuthorityChangedError } from 'coaction/shared';
try {
await store.getState().submitOrder({ idempotencyKey });
} catch (error) {
if (error instanceof ActionAuthorityChangedError) {
// 按 key 对账;不要盲目重复非幂等 action。
}
}Reconnect callback 按 generation 隔离,因此旧连接的迟到 callback 不能覆盖新连接已建立的状态。