AngularFire firestore get / snapshotchanges / valuechanges 对 observable 的操作不是异步的吗?

Posted

技术标签:

【中文标题】AngularFire firestore get / snapshotchanges / valuechanges 对 observable 的操作不是异步的吗?【英文标题】:AngularFire firestore get / snapshotchanges / valuechanges action on observable is not async? 【发布时间】:2021-01-23 07:27:18 【问题描述】:

您好,我的行为很奇怪。

我正在迭代一些文档并设置一些承诺,即当获取文档时,UI 会更新。

然而,虽然 promise 是原子的,但 firestore / AngularFire 会等待所有的 promise。

例子:

 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`)
           
          )
      

人们会期望随着数据的到来,对于每个承诺,它会慢慢打印完成的承诺。

但是它们都被打印一次。看起来这些承诺不是一个接一个,而是“一次完成”。在打印第一个控制台日志之前,等待时间很长,然后所有的 Promise 都会打印出来。

所以我希望如果我有一个进度条会增加一点点但很少但会立即增加

内部调用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;
          , []);
        ))

现在,如果我在 for 循环中放置一个 await,当然这可以正常工作并完成应有的承诺,但是这需要很多时间。

我也试过不使用AngularFire,使用原生的JS SDK效果一样。

我怀疑 IndexedDB 可能会导致这个或其他一些 Firebase 逻辑。

我在这里缺少什么,如果可能的话,我怎样才能获得所需的行为?

您可以通过 ["users" -> "events" -> "something"] firestore 集合复制此内容,如果每个“用户”有 500 个“事件”,并且每个事件还有 2 个文档。

因此,为用户获取所有事件并尝试为每个事件做出一个承诺,该承诺将在 for 数组中返回 2 个“某物”文档)

【问题讨论】:

不确定提供的代码是否足够。你能分享完整的getEventActivitiesAndSomeStreamsprocessStreamDocumentSnapshot 方法吗? @DipenShah processStreamDocumentSnapshot 只是一个 JSON 到类实例,只不过是从 JSON 返回中实例化一个对象。 getEventActivitiesAndSomeStreams 调用我上面写的“基本上”,它只有我在这里硬编码的 collection 名称等 您可以通过“用户”->“事件”->“某事”重现此内容,其中每个“用户”可以说 500 个“事件”,每个事件还有 2 个文档。因此,为用户获取所有事件并尝试为每个事件做出承诺,该承诺将在 for 数组中返回 2 个“某物”文档 为什么你会期望这里的东西一一迭代?您在一个 for 循环中激活所有流,这对于人类感知来说几乎是瞬时的,这意味着它们都基本上同时发出请求,因此它们应该基本上同时得到所有响应。您没有 async / await 语句或其他流控制工具告诉它一个一个地执行它们...如果您输入有关您想要发生的任何细节,那么我可以提供帮助,但就目前而言,一切都是行为完全符合预期。 【参考方案1】:

这种行为是意料之中的,与 firebase 毫无关系。您正在遍历数组并发送请求。项目之间没有等待或延迟,因此 for 循环(没有 await 语句)将在极短的时间内完成,这意味着所有请求都在几毫秒内发送出去,或者基本上同时发送.所以他们的回应也应该基本同时到达。

您已声明不想使用 await 语句并逐个迭代,因此很难确切知道您想要或期望发生什么。也许您希望它们相隔 0.5 秒?如果是这样,您需要编写该逻辑:

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`)
)

(删除的承诺会导致 idk 为什么首先使用它们,而使用 rxjs IMO 更容易进行这种控制)

【讨论】:

hmmm 但问题没有完成循环。是不是所有的“然后”的承诺都会在很长的等待时间内立即触发。 这就是异步编程的工作原理。循环基本上同时发出所有请求,然后所有请求基本上同时返回,并且只要响应进入,then 处理程序就会运行。它不关心何时或如何发送。 “漫长的等待时间”就是处理请求所需的时间。如果发送一个或几个请求要快得多,那么您可能只是因为一次请求太多而对浏览器施加了压力。 是的,我知道,但又一次。想象一个下载​​文件的进度条。这不是意味着其中一个 promises ,最小尺寸的那个,会首先进入“then”部分吗?这里几乎同时进入“then”部分。我们谈论的是一次完成的 500*1Mb 文件(API 读取)。这真的是设计的吗?我错过了什么? 在响应时间有显着变化的文件下载的情况下,是的,您会看到这一点。但是在您的情况下,所有请求恰好花费了大致相同的时间。但是,如果您一次发出 500 个请求,那么您对浏览器的要求就太高了。 一次 500 个请求也可能只是导致交通堵塞,因为您将开始遇到浏览器可以处理的阻塞点。您可能想探索批处理或最好设计为不一次发出那么多请求。想到的第一个解决方案是从 API 请求事件列表,而不是为每个单独的事件发出请求。

以上是关于AngularFire firestore get / snapshotchanges / valuechanges 对 observable 的操作不是异步的吗?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Firestore/Angularfire2 或 Firestore SDK 查询集合中的内部对象 ID

angularfire2:未启用 Cloud Firestore API

在 Firestore 中使用 AngularFire 列出文档的子集合

typescript angularfire firestore

在 AngularFire 中检索嵌套 Firestore 查询的 RxJS Observable

AngularFire2 firestore take(1) on doc valueChanges