【问题标题】:Wait for all Firebase data query requests before executing code在执行代码之前等待所有 Firebase 数据查询请求
【发布时间】:2021-07-30 12:16:53
【问题描述】:

我试图在处理它们并将它们应用于批处理之前提前从我的云 Firestore 数据库中的不同集合中获取数据,我创建了两个异步函数,一个用于捕获数据,另一个仅在所有数据之后执行某些代码被收集,我不希望代码在获取数据之前执行并创建错误,当我在异步函数收集数据完成后尝试访问matchesObject时,它一直说“它无法访问未定义的属性matchStatus”,我想用异步和等待来解决这个问题?任何人都可以解释一下为什么它在某一刻是未定义的

axios.request(options).then(function(response) {
  console.log('Total matches count :' + response.data.matches.length);

  const data = response.data;
  var matchesSnapshot;
  var marketsSnapshot;
  var tradesSnapshot;
  var betsSnapshot;

  matchesObject = {};
  marketsObject = {};
  tradesObject = {};
  betsObject = {};

  start();

  async function checkDatabase() {
    matchesSnapshot = await db.collection('matches').get();
    matchesSnapshot.forEach(doc => {
      matchesObject[doc.id] = doc.data();
      console.log('matches object: ' + doc.id.toString())
    });

    marketsSnapshot = await db.collection('markets').get();
    marketsSnapshot.forEach(doc2 => {
      marketsObject[doc2.id] = doc2.data();
      console.log('markets object: ' + doc2.id.toString())
    });

    tradesSnapshot = await db.collection('trades').get();
    tradesSnapshot.forEach(doc3 => {
      tradesObject[doc3.id] = doc3.data();
      console.log('trades object: ' + doc3.id.toString())
    });

    betsSnapshot = await db.collection('bets').get();
    betsSnapshot.forEach(doc4 => {
      betsObject[doc4.id] = doc4.data();
      console.log('bets object: ' + doc4.id.toString())
    });
  }


  async function start() {
    await checkDatabase();
    // this is the part which is undefined, it keeps saying it cant access property matchStatus of undefined
    console.log('here is matches object  ' + matchesObject['302283']['matchStatus']);

    if (Object.keys(matchesObject).length != 0) {
      for (let bets of Object.keys(betsObject)) {

        if (matchesObject[betsObject[bets]['tradeMatchId']]['matchStatus'] == 'IN_PLAY' && betsObject[bets]['matched'] == false) {
          var sfRef = db.collection('users').doc(betsObject[bets]['user']);
          batch11.set(sfRef, {
            accountBalance: admin.firestore.FieldValue + parseFloat(betsObject[bets]['stake']),
          }, {
            merge: true
          });

          var sfRef = db.collection('bets').doc(bets);
          batch12.set(sfRef, {
            tradeCancelled: true,
          }, {
            merge: true
          });
        }
      }
     
     }

    });

【问题讨论】:

  • 该错误消息暗示 /matches/302283 不在您的数据库中。
  • 我检查过了,它在那里,可能是什么问题?一瞬间它的未定义和它找到价值的净瞬间

标签: javascript firebase google-cloud-firestore async-await


【解决方案1】:

当前代码中有太多小问题,无法尝试逐个调试,因此此重构引入了针对您的数据的各种测试。它目前不会对您的数据库进行任何更改,旨在替代您的 start() 函数。

与您当前代码的主要区别之一是它不会不必要地下载 4 个文档集合(其中两个甚至没有在您包含的代码中使用)。

步骤

首先,它将获取所有具有matched == false 的投注文件。从这些文档中,它将检查它们是否有任何语法错误并将它们报告给控制台。对于每个有效的投注文档,将获取其链接的比赛文档的 ID,以便我们可以获取我们实际需要的所有比赛文档。然后我们将用户余额和投注文档的更改排队。最后,我们报告要完成的任何更改并提交它们(一旦您取消注释该行)。

代码

注意: fetchDocumentById()defined in this gist。它是一个帮助函数,允许someCollectionRef.where(FieldPath.documentId(), 'in', arrayOfIds) 一次获取超过 10 个 ID。

async function applyBalanceChanges() {

  const betsCollectionRef = db.collection('bets');
  const matchesCollectionRef = db.collection('matches');
  const usersCollectionRef = db.collection('users');

  const betDataMap = {}; // Record<string, BetData>

  await betsCollectionRef
    .where('matched', '==', false)
    .get()
    .then((betsSnapshot) => {
      betsSnapshot.forEach(betDoc => {
        betDataMap[betDoc.id] = betDoc.data();
      });
    });
    
  const matchDataMap = {}; // Record<string, MatchData | undefined>
  
  // betIdList contains all IDs that will be processed
  const betIdList = Object.keys(betDataMap).filter(betId => {
    const betData = betDataMap[betId];
    
    if (!betData) {
      console.log(`WARN: Skipped Bet #${betId} because it was falsy (actual value: ${betData})`);
      return false;
    }
    
    const matchId = betData.tradeMatchId;
    if (!matchId) {
      console.log(`WARN: Skipped Bet #${betId} because it had a falsy match ID (actual value: ${matchId})`);
      return false;
    }
    
    if (!betData.user) {
      console.log(`WARN: Skipped Bet #${betId} because it had a falsy user ID (actual value: ${userId})`);
      return false;
    }
    
    const stakeAsNumber = Number(betData.stake); // not using parseFloat as it's too lax
    if (isNaN(stakeAsNumber)) {
      console.log(`WARN: Skipped Bet #${betId} because it had an invalid stake value (original NaN value: ${betData.stake})`);
      return false;
    }
    
    matchDataMap[matchId] = undefined; // using undefined because its the result of `doc.data()` when the document doesn't exist
    return true;
  });
  
  await fetchDocumentsById(
    matchesCollectionRef,
    Object.keys(matchIdMap),
    (matchDoc) => matchDataMap[matchDoc.id] = matchDoc.data()
  );
  
  const batch = db.batch();
  const queuedUpdates = 0;

  betIdList.forEach(betId => {
    const betData = betDataMap[betId];      
    const matchData = matchDataMap[betData.tradeMatchId];
    
    if (matchData === undefined) {
      console.log(`WARN: Skipped /bets/${betId}, because it's linked match doesn't exist!`);
      continue;
    }
    
    if (matchData.matchStatus !== 'IN_PLAY') {
      console.log(`INFO: Skipped /bets/${betId}, because it's linked match status is not "IN_PLAY" (actual value: ${matchData.matchStatus})`);
      continue;
    }
    
    const betRef = betsCollectionRef.doc(betId);
    const betUserRef = usersCollectionRef.doc(betData.user);
    
    batch.update(betUserRef, { accountBalance: admin.firestore.FieldValue.increment(Number(betData.stake)) });
    batch.update(betRef, { tradeCancelled: true });
    queuedUpdates += 2; // for logging
  });
  
  console.log(`INFO: Batch currently has ${queuedUpdates} queued`);

  // only uncomment when you are ready to make changes
  // batch.commit();
}

用法:

axios.request(options)
  .then(function(response) {
    const data = response.data;

    console.log('INFO: Total matches count from API:' + data.matches.length);

    return applyBalanceChanges();
  }

【讨论】:

  • 谢谢,让我试试这个,如果文档值有问题你就跳过,但是为什么会发生这种情况呢?
  • @mac 有了适当的安全规则和验证,它们永远不会被跳过。我没有您的数据库的屏幕截图可供使用,因此我不得不做出一堆假设来进行测试。虽然您可以假设数据是正确的,但这可能会导致危险的错误。另外,通过记录它们,如果它最终成为数据库中数百个文档中的一个文档,它应该有助于找到您的问题所在。
猜你喜欢
  • 1970-01-01
  • 2021-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-28
  • 2022-01-05
  • 2015-06-02
  • 2021-11-21
相关资源
最近更新 更多