Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
154 views
in Technique[技术] by (71.8m points)

reactjs - How to make asynchronous calls inside of a firestore .forEach loop?

Im using the firebase admin SDK server-side, and in one of my routes, im getting a collection, and then looping over each document in said collection...during each iteration, i use a property value to do a second get(). in the then() of this secondary get(), i again use data from the initial get to make final, tertiary get().

unfortunately the asynchronous nature of these nested calls seems to be creating undesirable outcomes.

heres the route function:

router.get('/list', authorization, (req, res) => {
  console.log('/reports/list entered...')

  admin
    .firestore()
    .collection('user-reports')
    .get()
    .then(querySnapshot => {
      const reports = []
      querySnapshot.forEach(snapshotDocument => {
        const closed = snapshotDocument.get('closed')
        console.log(`closed status: ${closed}`)

        if (closed === false) {
          const data = snapshotDocument.data()
          console.log(`condition passed, data: ${JSON.stringify(data)}`)
          // get # of reports made by sender
          admin
            .firestore()
            .collection('users')
            .doc(data.reportee)
            .get()
            .then(doc => {
              data['reportee'] = {
                reportActivity: doc.get('reportActivity')
              }
              console.log(`first then => data; ${JSON.stringify(data)}`)
              // get report history of reportee
             
              admin
                .firestore()
                .collection('users')
                .doc(data.reporter)
                .get()
                .then(doc => {
                  data['reporter'] = {
                    reportActivity: doc.get('reportActivity')
                  }
                  console.log(`second then, ${JSON.stringify(data)}`)
                  reports.push(data)
                })
                .catch(err => {
                  return res.json({ error: true, message: err })
                })
            })
            .catch(err => {
              return res.json({ error: true, message: err })
            })
        }
      })
      console.log(`pre-response: ${JSON.stringify(reports)}`)
      return res.json({ reports })
    })
    .catch(err => res.json({ error: true, message: err }))
})

what im logging is the "first condition passed", "pre-response: []", and "first then => data". by the time i ultimately return "reports" its empty. is there a more effective way to run firestore methods inside of foreach loops?

question from:https://stackoverflow.com/questions/66056095/how-to-make-asynchronous-calls-inside-of-a-firestore-foreach-loop

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The solution almost always is to use Promise.all() to wait for a bunch of promises to resolve:

  rturn Promise.all(querySnapshot.map(snapshotDocument => {
    const closed = snapshotDocument.get('closed')
    console.log(`closed status: ${closed}`)

    if (closed === false) {
      const data = snapshotDocument.data()
      console.log(`condition passed, data: ${JSON.stringify(data)}`)
      // get # of reports made by sender
      return admin
        .firestore()
        .collection('users')
        .doc(data.reportee)
        .get()
        .then(doc => {
          data['reportee'] = {
            reportActivity: doc.get('reportActivity')
          }
          console.log(`first then => data; ${JSON.stringify(data)}`)
          // get report history of reportee
         
          return admin
            .firestore()
            .collection('users')
            .doc(data.reporter)
            .get()
            .then(doc => {
              data['reporter'] = {
                reportActivity: doc.get('reportActivity')
              }
              console.log(`second then, ${JSON.stringify(data)}`)
              return data;
            })
            .catch(err => {
              return res.json({ error: true, message: err })
            })
        })
        .catch(err => {
          return res.json({ error: true, message: err })
        })
    }
  })
  .then((reports) => {
    console.log(`pre-response: ${JSON.stringify(reports)}`)
    return res.json({ reports })
  })

There might be some syntax errors, but the basic changes:

  1. Turn the documents into a list of promises/results with querySnapshot.map(snapshotDocument => {.
  2. Use Promise.all to create a promise that resolves when all these results are done.
  3. Returns results from as deep down as needed all the way up to the main code.

Note that nesting of promises the way you do here is unusual and an anti-pattern, so in recommend also looking into learning to chain promises (so that you end up with a single catch - instead of having them all over the place.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...