AngularFire firestore get / snapshotchanges / valuechanges action on observable is not async?

Viewed 448

Hi I am having a very strange behaviour.

I am iterating over some documents and setting some promises that when the documents are fetched the UI is updated.

However, while the promises are atomic, the firestore / AngularFire waits for all the promises.

Example:

 for (const event of events) {
        this.eventService.getEventActivitiesAndSomeStreams(this.user,
          event.getID(),
          [DataLatitudeDegrees.type, DataLongitudeDegrees.type])
          .pipe(take(1)).toPromise().then((fullEvent) => {
            this.logger.info(`Promise completed`)
           
          })
      }

One would expect that slowly for each promise as the data comes it would print the promise completed.

However they are all printed as once. It doesn't look that those promises come one by one but "all at once". There is a big waiting time till the first console log is printed and then all promises print that.

So I would expect if I have a progress bar to increase little but little but increases at once

The inner call this.eventService.getEventActivitiesAndSomeStreams

return this.afs
        .collection('users')
        .doc(userID)
        .collection('events')
        .doc(eventID)
        .collection('activities')
        .doc(activityID)
        .collection('streams', ((ref) => {
          return ref.where('type', 'in', typesBatch);
        }))
        .get()
        .pipe(map((documentSnapshots) => {
          return documentSnapshots.docs.reduce((streamArray: StreamInterface[], documentSnapshot) => {
            streamArray.push(this.processStreamDocumentSnapshot(documentSnapshot)); // Does nothing rather to create an class of the JSON object passed back from the firestore
            return streamArray;
          }, []);
        }))

Now, if I put an await inside the for loop of course this works as it should going and completing the promises as it should, but then it takes a lot of time.

I also tried to not use AngularFire and use the native JS SDK with the same effect.

I am suspecting that the IndexedDB can be causing this or some other Firebase logic.

What am I missing here, and how can I have the desired behaviour if possible?

You could repro this via a ["users" -> "events" -> "something"] firestore collections, were each "user" has lets say 500 "events" and each of those events has 2 more docs.

So get all the events for the user and try to make for each one a promise that will return 2 documents of "something" inside a for array )

1 Answers

This behavior is pretty expected and has nothing at all to do with firebase. you're iterating over an array and sending out requests. there is no waiting or delay between items, so the for loop (without await statements) will finish in an imperceptibly small amount of time, which means all of the requests are being sent out within milliseconds of each other, or basically at the same time. So their responses should be expected to arrive at basically the same time as well.

You've stated that you don't want to use await statements and iterate one by one, so it's tough to know exactly what you do want or expect to happen. maybe you want them to be spaced .5 second apart? If so, you need to write that logic:

timer(0, 500).pipe( // put whatever ms time between requests you want here?
  take(events.length),
  switchMap(i => {
    return this.eventService.getEventActivitiesAndSomeStreams(this.user,
      events[i].getID(),
      [DataLatitudeDegrees.type, DataLongitudeDegrees.type]).pipe(take(1))
  })
).subscribe(fullEvent => {
  this.logger.info(`Promise completed`)
})

(removed promises cause idk why they're being used in the first place and this kind of control is easier with rxjs IMO)

Related