【问题标题】:How to wait for a forEach to finish before returning from my promise / function如何在从我的承诺/功能返回之前等待 forEach 完成
【发布时间】:2021-03-08 23:40:44
【问题描述】:

Angular、Firestore

我有一个角度函数来从一个 firestore 集合中获取产品,然后我循环该查询的结果以从另一个集合中查找价格。

在从外部承诺和外部函数本身返回之前,我如何才能等到 forEach 的价格完成?

返回的结果包含一个products数组,但是每个product的prices数组都是空的。

 const products = await this.billingService.getProducts();
async getProducts() {
    let result = [];
    let product = {};
    return this.db.collection(
      'products',
      ref => { ref
        let query: Query = ref;
          return query.where('active', '==', true)
      })
      .ref
      .get()
      .then(function (querySnapshot) {
        querySnapshot.forEach(async function (doc) {
          product = doc.data();
          product['prices'] = [];

          await doc.ref
            .collection('prices')
            .orderBy('unit_amount')
            .get()
            .then(function (docs) {
              // Prices dropdown
              docs.forEach(function (doc) {
                const priceId = doc.id;
                const priceData = doc.data();
                product['prices'].push(priceData);
              });
            });
        });
        result.push(product);
        return result;
      });
  }

我也尝试过这种方法,但不确定如何访问结果

await this.billingService.getProducts().then(results =>
getProducts() {
      const dbRef =
        this.db.collection(
          'products',
          ref => { ref
            let query: Query = ref; return query.where('active', '==', true)
        });
       const dbPromise = dbRef.ref.get();

      return dbPromise
        .then(function(querySnapshot) {
          let results = [];
          let product = {};
          querySnapshot.forEach(function(doc) {
            let docRef = doc.ref
              .collection('prices')
              .orderBy('unit_amount')
              results.push(docRef.get())
          });
          return Promise.all(results)
        })
        .catch(function(error) {
            console.log("Error getting documents: ", error);
        });
    } 

【问题讨论】:

  • async/await 无法按照您在 forEach 循环中所期望的方式工作。 stackoverflow.com/questions/37576685/…
  • 停止使用.forEach()。它不是异步或承诺感知的。使用常规的 for 循环。
  • @jfriend00 for(const doc of querySnapshot) { 类型 'QuerySnapshot' 必须有一个返回 iterator.ts(2488) 的 '[Symbol.iterator]()' 方法
  • @dak - querySnapshot.docs 是您应该能够使用的数组。见doc

标签: javascript google-cloud-firestore promise


【解决方案1】:

此版本的代码有效:

  getProducts(): Promise<any> {
    return new Promise((resolve,reject)=> {
      let result = [];
      let product = {};
      this.db.collection(
        'products',
        ref => { ref
          let query: Query = ref;
          return query.where('active', '==', true)
        })
        .ref
        .get()
        .then(async function (querySnapshot:firebase.firestore.QuerySnapshot) {
          for(const doc of querySnapshot.docs) {
            const priceSnap = await doc.ref
              .collection('prices')
              .orderBy('unit_amount')
              .get()
            product = doc.data();
            product['prices'] = [];
            // Prices dropdown
            for(const doc of priceSnap.docs) {
              const priceId = doc.id;
              let priceData = doc.data();
              priceData['price_id'] = priceId;
              product['prices'].push(priceData);
              resolve(result);// returns when it reaches here
            };
            result.push(product);
          };
        });
    })
  }

【讨论】:

    【解决方案2】:

    发布为社区 Wiki 答案,基于 cmets。

    对于这种情况,使用forEach() 不是正确的选择。正如在这种情况下here 所阐明的那样,forEach() 不能与await 函数一起正常工作,这样,你的承诺就不能正常工作。考虑到这一点以及您希望按顺序读取数据的事实——因为一个查询的结果会影响第二个查询——你需要使用普通的for 来循环遍历数据和数组。这个具体的答案here 应该可以帮助您提供代码示例。

    【讨论】:

      【解决方案3】:

      让 getProducts() 函数成为一个承诺。因此它只会在你解决它(或拒绝它)时返回。

      getProducts() {
      return new Promise((resolve,reject)=> {
      let result = [];
      let product = {};
      this.db.collection(
        'products',
        ref => { ref
          let query: Query = ref;
            return query.where('active', '==', true)
        })
        .ref
        .get()
        .then(function (querySnapshot) {
          querySnapshot.forEach(async function (doc) {
            product = doc.data();
            product['prices'] = [];
      
            doc.ref
              .collection('prices')
              .orderBy('unit_amount')
              .get()
              .then(function (docs) {
                // Prices dropdown
                docs.forEach(function (doc) {
                  const priceId = doc.id;
                  const priceData = doc.data();
                  product['prices'].push(priceData);
                });
                resolve(result);// returns when it reaches here
              });
          });
          result.push(product);
        });
       })
      }
      

      然后你可以使用 then 或 await 调用 Promise

      this.billingService.getProducts().then( res => {
       const products = res;
      })
      

      使用等待

      const products = await this.billingService.getProducts();
      

      【讨论】:

      • 多个问题。
      • 我只是在展示他可以使用承诺来做到这一点。他只需要在正确的地方解决。感谢您指出问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-08-15
      • 2016-05-08
      • 2015-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多