递归遍历嵌套对象数组时如何仅在全量遍历完成后返回一次结果
问题原因
加async/await完全解决不了你的问题,属于方向判断错误。
你遇到的多次return、控制台多次输出返回数组的现象,是递归的运行机制导致的:每一层递归调用都会生成独立的函数执行上下文,当前层级的循环遍历完成后,必然会走到函数末尾的return arr语句,这个逻辑和同步/异步没有任何关系。如果你在return语句前加了日志打印,那每结束一层递归就会输出一次数组,自然会看到多次打印结果。
额外说明:你原本的代码逻辑其实已经可以正确收集到所有叶子节点——因为传入的arr是引用类型,所有递归层操作的都是同一个数组实例,中间每一层return返回的都是同一个数组的引用,并不是返回了错误的分段结果。
修正方案
遍历嵌套树结构是纯同步操作,完全不需要改成async函数,以下两种写法都可以实现「遍历完所有节点后仅返回一次最终结果」的需求:
方案1:纯函数写法(推荐)
不需要调用方提前传入空数组,无外部依赖,不会产生共享引用的副作用:
interface Car { code?: string; children?: Car[]; [key: string]: any; } function getLeafNodes(nodes: Car[]): Car[] { const leafList: Car[] = []; for (const node of nodes) { const hasValidChildren = Array.isArray(node.children) && node.children.length > 0; if (hasValidChildren) { // 递归收集子分支的叶子节点,拼接到当前结果中 leafList.push(...getLeafNodes(node.children)); continue; } // 无有效子节点且存在code属性,判定为目标叶子节点 if (Object.prototype.hasOwnProperty.call(node, 'code')) { leafList.push(node); } } return leafList; } // 调用示例 const result = getLeafNodes(yourRootArray); // 直接拿到完整的叶子节点数组
方案2:沿用原有传参累加的写法
如果你要保留传入数组累加的逻辑,注意不要在递归函数内部加打印日志的逻辑,仅在最外层调用时接收最终结果即可:
async function getChildren( obj: Array<Car>, arr: Array<Car> = [], // 给arr设置默认空数组,简化调用 ) { for (const subObj of obj) { if ( Object.prototype.hasOwnProperty.call(subObj, 'children') && subObj.children instanceof Array && subObj.children.length > 0 ) { await getChildren(subObj.children, arr); } else if ( Object.prototype.hasOwnProperty.call(subObj, 'code') ) { arr.push(subObj); } } // 注意:不要在此处加console.log,否则每一层递归结束都会触发打印 return arr; } // 调用示例 const result = await getChildren(yourRootArray); // 仅最外层拿到最终完整结果 console.log(result); // 此处只会打印一次
关键提醒
- 网传「改成async可以解决递归多次return」是错误结论,async只会把返回值包装为Promise对象,不会改变递归函数每层都会执行到return语句的本质。
- 非必要不要把纯同步的遍历逻辑改成async,会徒增不必要的性能开销。
内容的提问来源于stack exchange,提问作者Maximilian546489
相关产品推荐
相关产品推荐

