Await function never executes

Tsa*_*ary 0 node.js firebase typescript google-cloud-functions google-cloud-firestore

I have this function, that iterates through an array of strings, where each string represents a user's UID. The function is supposed to go to each user's profile, check for their most current reputation (same like SO), and then push a map of uid and reputation to a new array.

The second array always came out empty so I've placed a few logs to check what is happening. This is my function:

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 ');
  };
});
Run Code Online (Sandbox Code Playgroud)

the first which says 'user log ' +id always prints and it prints the id of the user as it should, so I know the first array is just fine. But none of the other logs print. ever. My first thought was that maybe I got the path wrong? But I've checked a million times, this is the path in which a user's profile would be in my DB.

for example, this could be a path to a profile: communities_data/hfd98HDKKhfEwe6W/profiles/bqSFS04LKJDbfhdwU

Any idea where it fails

jfr*_*d00 5

My guess is that you have a timing issue. .forEach() does not wait for your asynchronous operations to complete before going on to the next iteration (it does not look at the promise that your code is returning from the async callback) and thus after the .forEach() your second array will always be empty after the .forEach() loop because it has not yet been populated (the asynchonous calls in the loop have not yet finished).

So basically, you rarely ever want to use async/await inside a .forEach() callback because the loop doesn't respect it and you have no way of knowing outside the loop when everything is done.

尽管您没有为该代码显示更大的上下文,但是通常的解决方案是使用常规的forlop或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
}
Run Code Online (Sandbox Code Playgroud)

注意,必须声明包含函数,async以允许您awaitfor循环内部使用。


为了获得更好的性能,您还可以并行执行所有这些查找,并使用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);
    });
}
Run Code Online (Sandbox Code Playgroud)