NodeJS:async/await 没有 return 使用循环正确数据

NodeJS: async/await doesn't return data correctly with loops

我正在进行这些调用,以列出来自 google 活动目录

的组的用户
let globalGroups = null;
let groupMembers = null; 

await GetCustomerId(req, res).then( async () => {
      // GetGroups is async function saves groups in `globalGroups` variable
      await GetGroups(token).then( () => {
            globalGroups.forEach( async (group) => {
                  // GetGroupMembers is async function saves users in `groupMembers` variable
                  await GetGroupMembers(group.id, token).then( () => {
                        groupMembers.forEach( (member) => {
                             // here I log the `member` and have no issues here
                             if (usersIds.includes(member.id)) {
                                 let user = users.find( ({ id }) => id === member.id );
                                 user.group_ids.push(group.id);
                             }
                             else {
                                  member.group_ids = [];
                                  member.group_ids.push(group.id);
                                  users.push(member);
                                  usersIds.push(member.id);
                             }
                         })
                   })
            });
            // the issue is here without timeout it returns an empty array because it doesn't wait for the loop to finish
            console.log(users);
            res.status(200).json({"users": users}).send();
           }).catch(function(err) {
               console.log(err)
               res.status(500).json({"error": err}).send();
           });
});

这个 return 是一个空数组,除非我使用超时来 return 这样的响应

setTimeout( () => {
     console.log(users);
     res.status(200).json({"users": users, "next_page_link": "notFound"}).send();
}, 1000);

如何在不使用超时的情况下让它等到整个循环结束到 return 响应?

const GetCustomerId = async (req, res, next) => {
    try {
        let authorization = req.headers['authorization'].split(' ');
        if (authorization[0] !== 'Bearer') {
            return res.status(401).send();
        } else {
            await axios({
                url: 'https://admin.googleapis.com/admin/directory/v1/users?domain=&maxResults=1',
                method: 'get',
                headers: {
                    'Content-Type': "application/json",
                    'Authorization': ' Bearer ' + authorization[1]
                },
            })
                .then((response) => {
                    globalCustomerId = response.data.users[0].customerId
                })
                .catch(function(err) {
                    console.log(err);
                });
        }
    } catch (err) {
        console.log(err);
    }
} 

const GetGroups = async (token) => {
  try {
    await axios({
        url: 'https://admin.googleapis.com/admin/directory/v1/groups?customer=' + globalCustomerId,
        method: 'get',
        headers: {
            'Content-Type': "application/json",
            'Authorization': ' Bearer ' + token
        },
    })
        .then((response) => {
            globalGroups = response.data.groups;
        })
        .catch(function (err) {
            return res.status(500).json({"error": err}).send();
        });

} catch (err) {
    return res.status(403).json(err).send();
}

}

const GetGroupMembers = async (groupId, token) => {
  await axios({
    url: "https://admin.googleapis.com/admin/directory/v1/groups/" + groupId + "/members",
    method: 'get',
    headers: {
        'Content-Type': "application/json",
        'Authorization': ' Bearer ' + token
    },
})
    .then((response) => {
        groupMembers = null;
        groupMembers = response.data.members;
    })
    .catch(function (err) {
        return res.status(500).json({"error": err}).send();
    });
}

globalGroups.forEach( async (group) => {

.forEach 中的异步方法实际上并没有执行您可能希望它执行的操作。

通过本质上执行 array.forEach(async method),您将调用一堆异步调用,数组中的每个元素调用 1 个。它实际上并不是一个一个地处理每个调用然后最终解析。

切换到使用内部带有 await 的常规 for 循环,它将执行您想要的操作。

例如

for (const group of globalGroups) {
  await GetGroupMembers(group.id, token)
  groupMembers.forEach.....
}

你可以这样做来强制你的代码更同步(或者使用类似 Promise.all 的东西来提高效率,同时仍然是同步的)但是 代码的另一个问题是 你陷入了回调地狱,这导致了 less-readable 代码。

我强烈建议重构您的 Get* 方法,使它们 return 成为您需要的值。然后你可以做一些更干净的事情 predictable/deterministic 比如:

const globalCustomerId = await GetCustomerId(req, res);
const globalGroups = await GetGroups(token); //note: Promise.all could help here

for (const group of globalGroups) {
  const groupMembers = await GetGroupMembers(group.id, token)
  groupMembers.forEach.....


}
console.log(users);
res.status(200).json({"users": users}).send();

您可以将其包装在 try/catch 中以进行错误处理。这导致更清晰、更简洁、更可预测的执行顺序。