首页 > 解决方案 > 递归承诺 NodeJS 不会终止

问题描述

我编写了以下函数来重建包含递归的 JSON 对象。但是在执行时,它不会在执行时返回任何console.log内容,并且一次又一次地记录相同的组件而不会终止。任何人都可以帮我确定问题吗?

this.findOne = (id) => {
  return new Promise(resolve => {
    componentCollection.find(id).then(components => {
      if (components[0]) {
        let subComponents = components[0].components;
        for (let i = 0; i < subComponents.length; i++) {
          console.log(subComponents[i])
          subComponents[i] = resolve(this.findOne(subComponents[i].id))
        }
        components[0].components = subComponents;
      }
      resolve(components[0])
    })
  })
}

编辑: 组件包括仅包含 id 作为属性。这就是为什么我必须像这样递归地构建它。我尝试了另一种方式,结果是相同的无限日志:

this.findOne = async (id) => {
  let components = await componentCollection.find(id)
  if (components[0]) {
    let subComponents = components[0].components;
    for (let i = 0; i < subComponents.length; i++) {
      console.log(subComponents[i])
      subComponents[i] = await this.findOne(subComponents[i].id)
    }
    components[0].components = subComponents;
  }
  return components[0]
}

两者都尝试一次又一次地记录父组件的第一个子组件。

我尝试构建的对象:

{
  id:1,
  name:"comp1",
  components:[
    {
      id:2,
      name:"comp2",
      components:[
        {
          id:3,
          name:"comp3"
        },
        {
          id:4,
          name:"comp4"
        }
      ]
    }
  ]
}

控制台无限日志来自console.log(subComponents[i])

{
  id:2,
  name:"comp2",components:[..]
}

编辑:

findOne 的周边代码:

this.findone = () =>{}
module.exports = {
  findOne: this.findOne
};

我在控制器中将其称为:

const ComponentService = require('./cs');
const component = await ComponentService.findOne(id)//id={1,2,...}

标签: javascriptnode.jsrecursionpromiseasync-await

解决方案


这是一个使用 Promises 的版本。大概很容易将其转换为async-await. 它使用纯函数,而不是对象的方法。这不应该太难改变;我没有尝试过,但您可能必须存储一个本地引用才能this使其工作。

const findOne = (id) => new Promise (
  (resolve, reject) => 
    componentCollection .find (id) .then (
      ({components = [], ...rest}) => 
        Promise .all (components .map (({id}) => findOne (id))) .then (
          children => resolve ({...rest, components: children}), 
          reject
        ),
      reject
    )
)

findOne (5) .then (console .log, console .warn)

findOne (1) .then (console .log, console .warn)
.as-console-wrapper {min-height: 100% !important; top: 0}
<script>
// Dummy code just for demonstration
const componentCollection = {
  find: (n) => [1, 2, 3, 4] .includes (n) 
    ? Promise .resolve (
      n == 1
        ? {id: 1, name: 'comp1', components: [{id: 2}]}
      : n == 2
        ? {id: 2, name: 'comp2', components: [{id: 3}, {id: 4}]}
      : n == 3
        ? {id: 3, name: 'comp3'}
      : {id: 4, name: 'comp4'}
    ) 
    : Promise .reject (`Not found: ${n}`)
}  
</script>

这里的要点是用于Promise.all将子组件的 Promise 列表转换为单个 Promise,然后您可以附加到该 Promise 上。


推荐阅读