簡體   English   中英

使用 firebase 調用時如何在 forEach 中使用異步調用

[英]How to use async call inside forEach when using firebase calls

我的問題是我無法弄清楚如何使用 Firestore 使此代碼正常工作(不確定這是否無關緊要)。

實際代碼如下:

prestamoItems() {
      var myarray = [];
      var myobject = {};

      //here comes the first async method (works OK)

      fb.prestamosCollection
        .orderBy("fechaPrestamo", "desc")
        .get()
        .then(val => {
          if (!val.empty) {

            //here comes forEach

            val.docs.forEach(doc => {
              myobject = doc.data();
              myobject.id = doc.id;
              console.log("The doc id is " +myobject.id)

              //here comes second async call inside the forEach loop, but it doesnt wait for this 
              //to be finished, and immediately goes to the other step

              fb.equiposCollection.doc(myobject.id).get().then(eqp => {
                console.log("The doc id from the other collection is " +eqp.id)
              })

              myarray.push(myobject)
              console.log("myobject pushed to myarray")
            });


          }
        });
    }

請注意,我在來自另一個異步方法的 forEach 循環中調用了一個異步方法。 在代碼的每個變體中,我得到的 output (控制台日志)如下:

11:13:14.999 Prestamos.vue?18d2:71 The doc id is 1yTCUKwBvlopXX2suvVu
11:13:14.999 Prestamos.vue?18d2:78 myobject pushed to myarray
11:13:15.000 Prestamos.vue?18d2:71 The doc id is Z5TE15Fj3HFrn1zvceGe
11:13:15.000 Prestamos.vue?18d2:78 myobject pushed to myarray
11:13:15.000 Prestamos.vue?18d2:71 The doc id is JNN9aN65XE1tUTmlzkoJ
11:13:15.000 Prestamos.vue?18d2:78 myobject pushed to myarray
11:13:15.000 Prestamos.vue?18d2:71 The doc id is NF2hHCpM8leZezHbmnJx
11:13:15.001 Prestamos.vue?18d2:78 myobject pushed to myarray
11:13:15.364 Prestamos.vue?18d2:74 The doc id from the other collection is 1yTCUKwBvlopXX2suvVu
11:13:15.368 Prestamos.vue?18d2:74 The doc id from the other collection is Z5TE15Fj3HFrn1zvceGe
11:13:15.374 Prestamos.vue?18d2:74 The doc id from the other collection is JNN9aN65XE1tUTmlzkoJ
11:13:15.379 Prestamos.vue?18d2:74 The doc id from the other collection is NF2hHCpM8leZezHbmnJx

因此,forEach 循環不會等待其中的異步 function(這實際上是預期的行為,AFAIK)。

問題是如何讓它在將對象添加到數組之前等待內部調用完成? 提前致謝。

要么將代碼嵌套到then()回調中,這取決於先前的結果,要么將循環( forEach不支持異步)包裝在async塊中以在內部使用await 例如。:

fb.prestamosCollection
  .orderBy("fechaPrestamo", "desc")
    .get()
    .then(val => {
      if (!val.empty) {
        // wrap loop in async function call iife so we can use await inside
        (async () => {
          for (var i = 0; i < val.docs.length; i++) {
            const doc = val.docs[i];
            myobject = doc.data();
            myobject.id = doc.id;
            // this will be synchronous now
            let eqp = await fb.equiposCollection.doc(myobject.id).get();
            console.log(eqp.id);
            myarray.push(myobject)
          }
        })();
      }
    });

問題的根源在於您試圖將異步操作(等待 Firestore 返回值)轉換為同步操作。 這在 JavaScript 中以有意義的方式實際上是不可能的,而不會引起很多問題!

您需要在 .then .then()回調中填充數組並返回 promise作為 function 的結果。 任何調用prestamoItems() function 的調用者也必須使用 .then .then()回調來訪問底層的myarray值:

const _ = {
  async prestamoItems() {
    const val = await fb.prestamosCollection.orderBy("fechaPrestamo", "desc").get();
    if (val.empty) {
      return myarray
    }

    // Promise.all() will take a list of promises and will return their results once they have all finished.
    return await Promise.all(
      // Array.prototype.map() will take an existing array and, for each item, call the given function and return a new array with the return value of each function in that array.
      // This is functionally equivalent to making a new array and push()ing to it, but it reads a lot nicer!
      val.docs.map(async doc => {
        const myobject = doc.data();
        const eqp = await fp.equiposCollection.doc(myobject.id).get()
        // I presume you want to do something with eqp here
        return myobject
      })
    );
  }
}

上面的代碼示例使用Array.prototype.map()來取消myarray ,因為它不是必需的。

調用者必須像這樣使用此代碼:

_.prestamoItems().then((myarray) => {
   ...
})

Promise 是一種表示某個值可能在未來某個時間點可用的方式。 因此,您必須確保您與 promise 的任何交互都以假定該值不可立即使用的方式編寫。 最簡單的方法是使用async / await並確保返回 promise 對象。

只需將推向里面移動,然后像這樣

fb.equiposCollection.doc(myobject.id).get().then(eqp => {
   console.log("The doc id from the other collection is " +eqp.id)

   myarray.push(myobject)
   console.log("myobject pushed to myarray")
})

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM