等待功能永远不会执行

时间:2019-08-07 23:52:35

标签: node.js typescript firebase google-cloud-firestore google-cloud-functions

我具有此功能,该功能遍历字符串数组,其中每个字符串代表用户的UID。该功能应该转到每个用户的个人资料,检查其最新信誉(类似于SO),然后将uid和信誉映射推送到新数组。

第二个数组总是空着,所以我放置了一些日志来检查发生了什么。这是我的功能:

candidates.forEach(async id => {
  console.log('user log ' + id);

  const snap2 = await db.doc('communities_data/' + community.id + '/profiles/' + id).get();
  const user = snap2.data();
  console.log('user ' + user);

  if (user !== undefined) {
    console.log(user + ' just user');
    const reputation = (user.reputation as number);
    candidatesWithReputation.push(new UserAndReputation(id, reputation));
  } else {
    console.log('user undefined ');
  };
});

第一个显示'user log ' +id的列始终会打印,并会按需打印用户ID,因此我知道第一个数组就可以了。 但是没有其他日志打印。曾经。我最初的想法是,也许我走错了路?但是我已经检查了一百万次,这是用户个人资料在我的数据库中的路径。

例如,这可以是配置文件的路径: communities_data/hfd98HDKKhfEwe6W/profiles/bqSFS04LKJDbfhdwU

任何失败的主意

1 个答案:

答案 0 :(得分:5)

我的猜测是您遇到时间问题。 .forEach()在继续下一次迭代之前不会等待异步操作完成(它不会查看您的代码正在从异步回调返回的承诺),因此在.forEach()之后在.forEach()循环之后,数组将始终为空,因为它尚未被填充(循环中的异步调用尚未完成)。

因此,基本上,您很少想在async/await回调中使用.forEach(),因为循环不尊重它,并且在完成所有操作后您无法知道循环之外的情况。

虽然您没有为该代码显示更大的上下文,但是通常的解决方案是使用常规的for lop或for/of循环来等待await语句这样您就可以更轻松地知道何时完成所有操作。

这是一种方法:

async function someFunction() {

    // other code here


    for (let id of candidates) {
      try {
          console.log('user log ' + id);

          const snap2 = await db.doc('communities_data/' + community.id + '/profiles/' + id).get();
          const user = snap2.data();
          console.log('user ' + user);

          if (user !== undefined) {
            console.log(user + ' just user');
            const reputation = (user.reputation as number);
            candidatesWithReputation.push(new UserAndReputation(id, reputation));
          } else {
            console.log('user undefined ');
          };
      } catch(e) {
          console.log(e);
          // decide what to do upon error, 
          //     skip it and proceed?
          //     stop further processing?
      }
    }
    // candidatesWithReputation should now be valid here
    console.log(candidatesWithReputation);

   // other code here
}

请注意,必须声明包含函数async,以使您可以在await循环内使用for


为了获得更好的性能,您还可以并行执行所有这些查找,并使用Promise.all()查看它们何时全部完成:

function someFunction() {

    // other code here

    Promise.all(candidates.map(id => {
        return db.doc('communities_data/' + community.id + '/profiles/' + id).get().then(snap2 => {
            return snap2.data();
        }).catch(err => {
            // decide what to do about an error here
            // this implementation skips any queries with error and proceeds with the others
            return undefined;
        });
    })).then(users => {
        let candidatesWithReputation = [];
        for (user of users) {
            if (user !== undefined) {
                // I've not seen this "user.reputation as number" syntax??  Typescript?
                const reputation = (user.reputation as number);
                candidatesWithReputation.push(new UserAndReputation(id, reputation));
            }
        }
        return candidatesWithReputation;
    }).then(users => {
        // list of users with reputation here
        console.log(users);
        // further processing here
    }).catch(err => {
        console.log(err);
    });
}