Javascript 使用firebase调用时如何在forEach内部使用异步调用

Javascript 使用firebase调用时如何在forEach内部使用异步调用,javascript,vue.js,asynchronous,vuejs2,Javascript,Vue.js,Asynchronous,Vuejs2,我的问题是,我不知道如何使用Firestore使这段代码正常工作(不确定这是否无关) 实际代码如下所示: prestamoItems() { var myarray = []; var myobject = {}; //here comes the first async method (works OK) fb.prestamosCollection .orderBy("fechaPrestamo", "desc")

我的问题是,我不知道如何使用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")
            });


          }
        });
    }
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循环中调用一个异步方法。在代码的每个变体中,我得到的输出(控制台日志)如下所示:

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")
            });


          }
        });
    }
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循环没有等待内部的异步函数(这实际上是预期的行为,AFAIK)


问题是如何让它在将对象添加到数组之前等待内部调用完成?提前谢谢。

只需像这样向内推动即可

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")
})

就这样往里推

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")
})

要么将依赖于先前结果的代码嵌套到
then()
回调中,要么将循环(
forEach
不支持异步)包装在
async
块中,以利用
wait
内部。例如:

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)
          }
        })();
      }
    });
fb.prestamosCollection
.orderBy(“fechaPrestamo”、“desc”)
.get()
.然后(val=>{
如果(!val.empty){
//在异步函数调用iife中包装循环,以便在内部使用wait
(异步()=>{
对于(var i=0;i
要么将依赖于先前结果的代码嵌套到
then()
回调中,要么将循环(
forEach
不支持异步)包装在
async
块中,以便在内部使用
wait
。例如:

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)
          }
        })();
      }
    });
fb.prestamosCollection
.orderBy(“fechaPrestamo”、“desc”)
.get()
.然后(val=>{
如果(!val.empty){
//在异步函数调用iife中包装循环,以便在内部使用wait
(异步()=>{
对于(var i=0;i
问题的根源在于,您试图将非同步操作(等待Firestore返回值)转变为同步操作。在JavaScript中,如果不引起很多问题,这是不可能以有意义的方式实现的

您需要在
.then()
回调中填充数组,并作为函数的结果返回承诺。调用
prestamoItems()
函数的任何调用方也必须使用
。然后()
回调以访问底层
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) => {
   ...
})


承诺是一种表示价值在未来某个时刻可能可用的方式。因此,您必须确保您与承诺的任何交互都是以这样一种方式编写的,即假定该值不是立即可用的。最简单的方法是使用
async
/
wait
并确保返回承诺对象。

问题的根源在于,您试图将非同步操作(等待Firestore返回值)转变为同步操作。在JavaScript中,如果不引起很多问题,这是不可能以有意义的方式实现的

您需要在
.then()
回调中填充数组,并作为函数的结果返回承诺。调用
prestamoItems()
函数的任何调用方也必须使用
。然后()
回调以访问底层
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) => {
   ...
})


承诺是一种表示价值在未来某个时刻可能可用的方式。因此,您必须确保您与承诺的任何交互都是以这样一种方式编写的,即假定该值不是立即可用的。最简单的方法是使用
async
/
wait
并确保返回承诺对象。

将adding to数组放在
then
中。仍然是相同的结果,即使在第二个then()OP中推送,我假设在将元素推送到
myarray
之后,您打算使用它。您将如何使用它?在返回myarray后,我将把结果放在bootstrap vue表(b-table)中。将添加到
中的数组放在
中,然后
。仍然是相同的结果,即使在第二个then()OP中推送,我假设您打算在将元素推送到它之后使用
myarray
。您将如何使用它?返回MyArray后,我将把结果放在bootstrap vue表(b表)中。这是一个很好的答案,但接受的答案具有更简单的结构。无论如何,谢谢你的帮助,我们非常感谢你的帮助。接受的答案也有不同的功能:它将按顺序从数据库中获取每个项目,而不是并行,因此速度会慢得多。我一直在使用接受的答案,你是对的,它将按顺序进行,因此它将比大型集合的预期速度慢得多(我现在有一个小集合,只是为了测试的目的)。我将修改我的代码,因为你认为这是一个好的答案,但是被接受的答案有一个更简单的结构。谢谢