Files
Endoleap/src/lib/canvas-bindings/node-internals.ts
T

27 lines
1.5 KiB
TypeScript

/**
* 把节点端口/尺寸变化的测量请求合并给 renderer,避免同一轮重复调用更新。
* owner token 区分同 ID 的不同挂载实例;卸载实例的迟到失效不能触发后来新实例的测量。
*/
/** One renderer store's transient measurement requests. Not model or history state. */
/** 每个 renderer store 独立创建;默认微任务调度,也可注入测试或宿主 defer。 */
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 去重;先清 dirty 再回调,让重入失效进入下一轮。 */
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({
/** 仅活跃租约可以变脏,同一批最多安排一次延后 flush。 */
invalidate(){if(!owners.has(owner))return;dirty.add(owner);if(!queued){queued=true;defer(flush);}},
/** 卸载同时移除 owner 和其未执行请求;已排微任务可自然执行为空,不取得新实例权限。 */
dispose(){owners.delete(owner);dirty.delete(owner);},
});
},
});
}