返回未定义的递归异步函数

Recursive async function returning undefined

我正在制作一个 运行 宁 mysql 查询的递归异步函数。这是我正在使用的数据库:

+----+-------------------+-----------+----------+-----------+---------------------+
| id | task              | completed | parentid | createdby | createdat           |
+----+-------------------+-----------+----------+-----------+---------------------+
|  1 | Clean apartment   |         0 |     NULL |         1 | 2022-03-24 00:47:33 |
|  2 | Clean bathroom    |         0 |        1 |         1 | 2022-03-24 00:47:33 |
|  3 | Clean kitchen     |         0 |        1 |         1 | 2022-03-24 00:47:33 |
|  4 | Wash shower       |         0 |        2 |         1 | 2022-03-24 00:47:33 |
|  5 | Wash toilet       |         0 |        2 |         1 | 2022-03-24 00:47:33 |
|  6 | Clean glass panes |         1 |        4 |         1 | 2022-03-24 00:47:33 |
|  7 | Clean faucet      |         0 |        4 |         1 | 2022-03-24 00:47:33 |
|  8 | Clean sink        |         0 |        3 |         1 | 2022-03-24 00:47:33 |
|  9 | Take out trash    |         1 |        3 |         1 | 2022-03-24 00:47:33 |
+----+-------------------+-----------+----------+-----------+---------------------+

如果我将这个数据库存储在一个数组中,我可以 运行 这个函数:

function comp(tasks, taskId) {
    var task = tasks.find(task => task.id === taskId)
    var children = tasks.filter(t => t.parentId === taskId)

    task.children = children.map(child => comp(tasks, child.id));

    return task
}

递归地将子任务嵌套到主任务中。

现在的问题是我对异步函数的理解不是很好

这是我现在的进展:

async function comp(taskId) {
    // SELECT * FROM tasks WHERE id = taskId
    var task = await con.promise().query('select * from tasks where id = ' + taskId)

    
    // SELECT * FROM tasks WHERE parentId = taskId
    var children = await con.promise().query('select * from tasks where parentid = ' + taskId)

    
    task[0][0].children = children[0].map(child => {
        comp(child.id)
    })
    console.log(task[0])
    
}

但是这个 returns 带有未定义子项的任务:

[
  {
    id: 1,
    task: 'Clean apartment',
    completed: 0,
    parentid: null,
    createdby: 1,
    createdat: 2022-03-23T23:47:33.000Z,
    children: [ undefined, undefined ]
  }
]

简而言之,我要查找的结果如下所示:

{
    id: 1,
    task: 'Clean apartment',
    completed: 0,
    parentid: null,
    createdby: 1,
    createdat: 2022-03-23T23:47:33.000Z,
    children: [ 
        {
            id: 2,
            task: 'Clean bathroom',
            completed: 0,
            parentid: 1,
            createdby: 1,
            createdat: 2022-03-23T23:47:33.000Z,
            children: [ 
                {
                    id: 4,
                    task: 'Wash shower',
                    completed: 0,
                    parentid: 2,
                    createdby: 1,
                    createdat: 2022-03-23T23:47:33.000Z,
                    children: [ ... ]
                  },
                  {
                    id: 5,
                    task: 'Wash toilet',
                    completed: 0,
                    parentid: 2,
                    createdby: 1,
                    createdat: 2022-03-23T23:47:33.000Z,
                    children: [ ... ]
                  },
            ]
          },
          {
            id: 3,
            task: 'Clean kitchen',
            completed: 0,
            parentid: 1,
            createdby: 1,
            createdat: 2022-03-23T23:47:33.000Z,
            children: [ ... ]
          },
        
        
  }

有什么建议吗?

本质上,您的代码的唯一问题是您不等待异步函数 comp() 的结果。 map() 将 return 一个 Promise 数组,您将需要等待所有可以使用 Promise.all() 完成的承诺。 Promise.all() return 是一个 Promise,当传递给 Promise.all() 的数组中的所有 Promise 都已解决时,它将解析。如果您 await 您的子数组将按您预期的方式传播。

这是您使用 Promise.all() 的代码。因为目前我还没有准备好合适的数据库,所以我用对具有人为延迟 built-in 的函数的异步调用替换了对数据库的所有异步调用,因此您可以看到如何实际等待那些和那个结果还真是等着呢

const data = [
  {
    id: 1,
    task: "Clean apartment",
    completed: 0,
    parentid: null,
    createdby: 1,
    createdat: "2022-03-24 00:47:33",
  },
  {
    id: 2,
    task: "Clean bathroom",
    completed: 0,
    parentid: 1,
    createdby: 1,
    createdat: "2022-03-24 00:47:33",
  },
  {
    id: 3,
    task: "Clean kitchen",
    completed: 0,
    parentid: 1,
    createdby: 1,
    createdat: "2022-03-24 00:47:33",
  },
  {
    id: 4,
    task: "Wash shower",
    completed: 0,
    parentid: 2,
    createdby: 1,
    createdat: "2022-03-24 00:47:33",
  },
  {
    id: 5,
    task: "Wash toilet",
    completed: 0,
    parentid: 2,
    createdby: 1,
    createDate: "2022-03-24 00:47:33",
  },
  {
    id: 6,
    task: "Clean glass panes",
    completed: 1,
    parentid: 4,
    createdby: 1,
    createdat: "2022-03-24 00:47:33",
  },
  {
    id: 7,
    task: "Clean faucet",
    completed: 0,
    parentid: 4,
    createdby: 1,
    createdat: "2022-03-24 00:47:33",
  },
  {
    id: 8,
    task: "Clean sink",
    completed: 0,
    parentid: 3,
    createdby: 1,
    createdat: "2022-03-24 00:47:33",
  },
  {
    id: 9,
    task: "Take out trash",
    completed: 1,
    parentid: 3,
    createdby: 1,
    createdat: "2022-03-24 00:47:33",
  },
];

async function comp(tasks, taskId) {
  // do your DB calls here (here just imitating the delay but the function are async to the result will remain the same)
  var task = await queryFind(tasks, taskId);
  var children = await queryFilter(tasks, taskId);

  // map() returns an array of promises as comp() returns a promise
  // Promise.all() returns a Promise that returns when all promises within the array are settled
  task.children = await Promise.all(
    children.map((child) => comp(tasks, child.id))
  );

  return task;
}

// this function immitates an async DB access.
async function queryFind(tasks, taskId) {
  // wait for 100 milliseconds (imitate delay)
  await sleep(100);
  return tasks.find((task) => task.id === taskId);
}

// this function immitates an async DB access.
async function queryFilter(tasks, taskId) {
  // wait for 100 milliseconds (imitate delay)
  await sleep(100);
  return tasks.filter((t) => t.parentid === taskId);
}

// delay execution. should imitage network delay here
async function sleep(ms) {
  return new Promise((resolve) => setTimeout(() => resolve(), ms));
}

// Start at task with ID 1; need to wrap the function call in an async method to be able to use await
(async () => {
  const test = await comp(data, 1);
  console.log(JSON.stringify(test, null, 4));
})();

您正在等待最初的两个等待项目完成 运行,然后您开始下一个递归调用,然后打印而不等待递归调用本身。

首先,您需要

await comp(child.id);

但您还需要等待每个 child 完成 运行。

Promise.all(array)

将等待您传入的数组中的每个承诺完成,恰好 children[0].map(async () => {}) 将 return 一个承诺数组。去啊,(a)等待那个,你应该被设置。