深度多分支树递归JavaScript函数父引用丢失问题求解
问题原因
- 核心问题是没有等待数据库异步查询的结果,直接将返回的
Promise对象推入子节点数组,导致parentObj.children中存储的不是真实节点对象,而是未 resolve 的Promise实例。 - 递归处理第一个子节点(实际为 Promise 实例)时,
assembletree函数尝试访问parentObj.children属性,而 Promise 实例不存在该属性,直接抛出异常导致函数执行中断,后续子节点自然不会被处理。 - 额外隐患:原代码用循环串行执行异步数据库查询,IO 等待时间会随节点数量成倍增加,性能较差。
修复方案
修正后代码
async function caller(){ // 替换为实际获取根节点的逻辑 const rootParent = await getDatabaseObject(rootId); await assembletree(rootParent); // 执行完成后 rootParent 即为完整树形结构 } async function assembletree(parentObj){ // 并行获取所有子节点的真实对象,减少IO等待时间 const childFetchPromises = parentObj.children.map(childId => getDatabaseObject(childId)); const childrenArray = await Promise.all(childFetchPromises); // 直接替换原children的id数组为真实子节点数组,无需冗余的delete和assign操作 parentObj.children = childrenArray; // 并行递归处理所有子节点,性能优于串行循环 await Promise.all(parentObj.children.map(child => assembletree(child))); // 若需要串行处理子节点,可替换为以下写法,同样不会出现执行中断问题 // for (let index = 0; index < parentObj.children.length; index++) { // await assembletree(parentObj.children[index]); // } } // 封装数据库查询逻辑,确保返回Promise async function getDatabaseObject(id) { // 替换为实际的数据库查询逻辑 return await database.queryById(id); }
关键优化点
- 采用
Promise.all并行批量获取子节点,既保证拿到的所有对象都是 resolve 后的真实节点,也大幅提升了查询效率。 - 简化了子节点属性赋值逻辑,直接赋值即可实现覆盖,无需冗余操作。
- 递归阶段也可通过
Promise.all并行处理所有子节点的递归逻辑,进一步缩短整棵树的构建时间。 - 若需要避免单个节点查询失败导致整棵树构建失败,可在
getDatabaseObject中添加错误捕获逻辑,查询失败时返回null或者占位节点,后续再过滤掉无效节点即可。
内容的提问来源于stack exchange,提问作者Jeppe Christensen
相关产品推荐
相关产品推荐

