30 lines
2.0 KiB
TypeScript
30 lines
2.0 KiB
TypeScript
/**
|
||
* 把节点端口/尺寸变化的测量请求合并给 renderer,避免同一轮重复调用更新。
|
||
* owner token 区分同 ID 的不同挂载实例;卸载实例的迟到失效不能触发后来新实例的测量。
|
||
*/
|
||
/** One renderer store's transient measurement requests. Not model or history state. */
|
||
/** 每个 renderer store 独立创建:owners 记录挂载 token→nodeId,dirty 只记录待测 token。
|
||
* defer 默认为微任务;update 接收执行时仍有效的 ID,不接触文档。工厂没有全局 dispose/flush 公开入口。 */
|
||
export function createNodeInternalsBatch(update:(ids:string[])=>void,defer:(flush:()=>void)=>void=queueMicrotask){
|
||
const owners=new Map<symbol,string>(),dirty=new Set<symbol>();let queued=false;
|
||
/** 执行时解析仍有效 owner 并按 ID 去重,清 queued/dirty 后 update;重入 invalidate 可安排下一轮。
|
||
* update 异常直接传播,本批不自动回队重试;卸载 token 的请求被排除,新 token 须自行 invalidate 才入本批。 */
|
||
function flush(){
|
||
queued=false;const ids=new Set<string>();for(const owner of dirty){const id=owners.get(owner);if(id!==undefined)ids.add(id);}dirty.clear();
|
||
if(ids.size)update([...ids]);
|
||
}
|
||
return Object.freeze({
|
||
/** 每次挂载取得独立租约,即使节点 ID 相同也能单独 dispose。 */
|
||
acquire(id:string){
|
||
const owner=Symbol(id);owners.set(owner,id);
|
||
return Object.freeze({
|
||
/** 活跃 token 去重变脏,queued 防重复 defer;多个 token 若同 nodeId,flush 时再合成一个 ID。
|
||
* 不捕获 defer 异常:若宿主抛错但未排任务,queued 不自动复位,宿主应使用可靠调度端口。 */
|
||
invalidate(){if(!owners.has(owner))return;dirty.add(owner);if(!queued){queued=true;defer(flush);}},
|
||
/** 卸载同时移除 owner 和其未执行请求;已排微任务可自然执行为空,不取得新实例权限。 */
|
||
dispose(){owners.delete(owner);dirty.delete(owner);},
|
||
});
|
||
},
|
||
});
|
||
}
|