如何实现树形对象系统中嵌套属性子集解析完成的回调监听
树形对象系统watch3方法实现方案
需求回顾
现有树形对象系统中,原watch方法仅在节点所有子属性完成解析(resolved)时触发回调。需扩展实现watch3方法,支持通过类GraphQL的Matcher语法匹配嵌套属性子集,当该子集所有属性解析完成时触发回调,同时满足:
- 若添加监听前目标子集已全部解析,立即触发回调
- 若添加监听后目标子集逐步完成解析,待全部完成时触发回调
核心实现思路
- Matcher语法解析:将类GraphQL结构的Matcher转换为扁平化的属性路径集合(如
{ a, b: { c, d } }转换为['a', 'b.c', 'b.d']) - 状态追踪与触发判断:为每个监听任务维护已完成解析的路径计数,当计数等于总路径数时执行回调
- 即时检查机制:添加监听时遍历所有目标路径,统计已resolved的数量,若全部完成则立即执行回调
- 动态监听绑定:对未resolved的路径,绑定对应节点的解析完成事件,每完成一个路径就更新计数,直至全部完成后触发回调并清理监听
代码实现
假设现有系统基础结构如下:
class TreeNode { constructor() { this.resolved = false; this.children = new Map(); this.onResolvedCallbacks = []; } // 获取指定路径的节点,路径格式如'a.b.c' get(path) { let current = this; for (const key of path.split('.')) { if (!current.children.has(key)) return null; current = current.children.get(key); } return current; } // 标记当前节点为已解析,并触发回调 resolve() { if (this.resolved) return; this.resolved = true; this.onResolvedCallbacks.forEach(cb => cb()); } // 监听节点解析完成事件 onResolved(callback) { if (this.resolved) { callback(); return; } this.onResolvedCallbacks.push(callback); } }
实现watch3及辅助方法
class TreeWatcher { constructor(rootNode) { this.root = rootNode; } // 解析Matcher为扁平化路径集合 parseMatcher(matcher) { const paths = []; function traverse(obj, parentPath = '') { for (const [key, value] of Object.entries(obj)) { const currentPath = parentPath ? `${parentPath}.${key}` : key; if (typeof value === 'object' && value !== null && !Array.isArray(value)) { traverse(value, currentPath); } else { paths.push(currentPath); } } } traverse(matcher); return paths; } // 核心watch3方法 watch3(matcher, callback) { const targetPaths = this.parseMatcher(matcher); if (targetPaths.length === 0) { callback(); return; } let resolvedCount = 0; const totalPaths = targetPaths.length; const cleanupFns = []; // 检查单个路径的解析状态,并绑定监听 const checkAndWatchPath = (path) => { const node = this.root.get(path); if (!node) { // 路径不存在时默认标记为完成,可根据需求调整为抛出错误或其他逻辑 resolvedCount++; checkIfAllResolved(); return; } if (node.resolved) { resolvedCount++; checkIfAllResolved(); return; } // 绑定解析完成监听 const onResolved = () => { resolvedCount++; checkIfAllResolved(); }; node.onResolved(onResolved); cleanupFns.push(() => { const index = node.onResolvedCallbacks.indexOf(onResolved); if (index !== -1) node.onResolvedCallbacks.splice(index, 1); }); }; // 判断是否所有路径已解析完成 const checkIfAllResolved = () => { if (resolvedCount === totalPaths) { callback(); // 清理所有监听 cleanupFns.forEach(fn => fn()); } }; // 遍历所有目标路径,初始化检查与监听 targetPaths.forEach(checkAndWatchPath); } }
场景验证
场景1:添加监听前属性已全部解析
// 构建已解析完成的树 const root = new TreeNode(); const nodeA = new TreeNode(); nodeA.resolve(); root.children.set('a', nodeA); const nodeB = new TreeNode(); const nodeBC = new TreeNode(); nodeBC.resolve(); nodeB.children.set('c', nodeBC); nodeB.resolve(); root.children.set('b', nodeB); const watcher = new TreeWatcher(root); // 监听子集{a, b: {c}} watcher.watch3({ a: true, b: { c: true } }, () => { console.log('子集已全部解析完成(场景1)'); }); // 输出:子集已全部解析完成(场景1)
场景2:添加监听后属性逐步解析完成
const root = new TreeNode(); const nodeA = new TreeNode(); root.children.set('a', nodeA); const nodeB = new TreeNode(); const nodeBD = new TreeNode(); nodeB.children.set('d', nodeBD); root.children.set('b', nodeB); const watcher = new TreeWatcher(root); // 监听子集{a, b: {d}} watcher.watch3({ a: true, b: { d: true } }, () => { console.log('子集已全部解析完成(场景2)'); }); // 逐步解析节点 setTimeout(() => nodeA.resolve(), 1000); setTimeout(() => nodeBD.resolve(), 2000); // 2秒后输出:子集已全部解析完成(场景2)
内容的提问来源于stack exchange,提问作者Lance Pollard
相关产品推荐
相关产品推荐

