【问题标题】:Why does my recursive function, that involves async calls in a loop, not return to the caller function to continue a loop?为什么我的递归函数(涉及循环中的异步调用)不返回调用者函数以继续循环?
【发布时间】:2020-10-11 09:23:10
【问题描述】:

这是我尝试对包含许多文件的程序进行静态分析,所有文件都具有引用程序中其他文件的导入语句,以查找循环引用。 dependenciesCollection 是 [key, value] 条目的映射,文件名作为键,它们的依赖文件数组作为值。此代码在 node.js v12 中运行,我需要 async/await 为节点提供垃圾收集的机会,否则它会因堆栈溢出而崩溃。我无法让函数返回堆栈以返回调用函数并继续调用它的 for 循环。我做错了什么?

let cyclicReferences = [] // a global array of pathToken arrays
const parseCollection = async (currentPathTokens, fileName, dependenciesCollection, targetFile, report) => {
  // currentPathTokens has entries for the sequence of entries leading to this file
  let dependencies = dependenciesCollection.item(fileName) // get array of dependent file names to investigate
  // Some testing code here omitted for brevity
  for (let index = 0; index < dependencies.length; index++) {
    let nextFile = dependencies[index]
    currentPathTokens.push(nextFile) //traveled path so far
    await parseCollection(currentPathTokens, nextFile, dependenciesCollection, targetFile, report)
    // I want the called recursive function to return to continue the for loop here, but it does not.
    currentPathTokens.pop() // shorten the path to dependency's parent node
  }
  // At end of this for loop, return to the caller to continue the loop there.
  // Never happens! Always goes back to the very first caller, never an intermediate one.
}

【问题讨论】:

  • 你认为你可以将它重写为我们可以在嵌入中运行的东西吗?
  • 为什么这是一个异步函数?看起来它唯一的异步部分是递归函数本身。

标签: node.js recursion


【解决方案1】:

我通过将方法从使用递归解决方案更改为使用树形图的广度优先搜索 (BFS) 解决方案解决了我的问题。我的主要数据源是一个带有键值数组的地图对象。 48 个键中的每一个都是我项目中一个文件的名称。每个键的值都是导入的依赖文件的名称数组。所以这个映射可以被认为是一个树数据结构的表示,其中每个键是一个节点,每个值数组是一个子节点列表。每个键要么被视为根节点,要么被视为另一个根下的子节点。有关使用二叉树的讨论,请参阅 https://www.freecodecamp.org/news/all-you-need-to-know-about-tree-data-structures-bceacb85490c/。我的树不是二叉树,因为大多数节点都有两个以上的孩子。但是 freecodecamp 关于实现 BFS 算法的讨论提供了使用我的树所需的信息。队列数据结构是一个数组,可以容纳为每个级别添加的两个以上的子节点。我遍历主映射对象以选择每个键作为根节点,然后逐级处理它以检查每个子节点。如果我找到一个与根节点同名的子节点,那么我发现了一个需要修复的循环引用。

    let theQueue = []
    let cyclicReferencesCollection = new Collection()
    let targetFile // a reference to this file from a dependent is a cyclic reference
    dependenciesCollection.keys().forEach((fileName) => {
      targetFile = fileName // any reference back to here is cyclic
      let theNodeObject = { level: 0, name: `${fileName}`, path: [`${fileName}`] }
      theQueue.length = 0 // reset
      theQueue.push(theNodeObject)
      let filesQueued = [] // a list of child nodes inspected
      /*********start looping over levels from this tree root************* */
      while (theQueue.length > 0) {
        let theNodeObject = theQueue.splice(0, 1)[0] // get next node object from queue
        let thisLevel = theNodeObject.level + 1
        let thisFile = theNodeObject.name
        let currentPath = theNodeObject.path // current path array
        if (theQueue.length > 0 && targetFile === thisFile) {
          // when the queue is empty, the targetFile will equal thisFile
          cyclicReferencesCollection.add([...currentPath], targetFile)
          break // we found a cyclic reference, start the next root
        }
        let dependencies = dependenciesCollection.item(thisFile)
        // these are the child nodes
        if (dependencies) {
          dependencies.forEach((fileName) => {
            if (!filesQueued.includes(fileName)) {
              // don't queue child nodes already inspected
              filesQueued.push(fileName) // save name to prevent dup testing
              let theNodeObject = { level: thisLevel, name: `${fileName}`, path: [...currentPath, `${fileName}`] }
              theQueue.push(theNodeObject)
            }
          })
        }
      }
    })

【讨论】:

    猜你喜欢
    • 2012-03-30
    • 1970-01-01
    • 2012-05-15
    • 2018-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-14
    • 1970-01-01
    相关资源
    最近更新 更多