Angular-正确嵌套Observables及其订阅

时间:2018-07-12 01:37:33

标签: javascript angular nested rxjs observable

我正在使用angularFire2从Firebase提取数据作为可观察对象。这是我的代码的简化版本,下面对此有一些解释:

this.af.getObservable(`userChats/${this.userID}/`).subscribe((recentConversations) => {
  recentConversations.forEach(conversation => {
    this.af.getObservableSortByVar(`allChats/${conversation.conversationID}/`, "lastUpdate").subscribe((conversationData) => {

      let userKey, lastMessage, lastMessageText, lastMessageSender, lastMessageDate;

      for (var i = 0; conversationData.length > i; i++) {
        switch (conversationData[i].key) {
          case "messages": {
            lastMessage = (conversationData[i][Object.keys(conversationData[i])[Object.keys(conversationData[i]).length - 1]]);
            lastMessageText = lastMessage.message;
            lastMessageSender = lastMessage.sender;
            lastMessageDate = lastMessage.date;
          }
          case "users": {
            userKey = conversationData[i].userKey;
          }
        }
      }
      this.recentChats.push(this.createConversationObject("username", userKey, lastMessageSender, lastMessageText, lastMessageDate));
    });
  });
});

当前,我正在调用数据库以检索用户所有对话的列表。

  1. 我收到了我所订阅的所有会话的Observable对象,因为我想保持数据库中数据的最新状态。

  2. 然后,我遍历对话的“可观察”。我需要为每个迭代的元素(每次对话)进行新的数据库调用,以获得有关它的信息/元数据(内容,senderID,对话日期等)。因此,我得到了两个Observable-一个嵌套在另一个中,两个都已订阅。

  3. 从第二个可观察对象获得会话的内容/元数据后,我将获得的元数据作为一个对象推入名为“ recentChats”的数组中。

当我一次执行整个代码块(程序开始时的初始调用)时,这完成了工作。但是,当数据库中的数据被修改(数据库中的“ userChat”节点或“ allChats”节点,或两者都被激活)并且订阅被激活时,我得到了整个代码块的多个(重复)调用,用相同的结果多次淹没我的数组。

当我只想打一个电话刷新信息时,我会接到不必要的电话。

因此,我可以看到我对Observables的逻辑和理解不正确。有人可以解释上面这个例子的正确解决方案吗?如何在没有重复(相同)调用的情况下嵌套Observable订阅?

1 个答案:

答案 0 :(得分:1)

我认为,使用RxJS,您永远不必这样编写代码。

  

但是,当数据库中的数据被修改(数据库中的“ userChat”节点或“ allChats”节点,或两者都被激活)并且订阅被激活时,我得到了整个块的多次(重复)调用的代码多次淹没我的数组,结果相同。

每次外部可观察对象发出值时,您再次订阅每个内部可观察对象 。这意味着对于同一对话,您将具有多个被执行的订阅。

我建议使用运算符仅具有一个可观察且订阅一次

示例(使用RxJS 6语法,如果您低于5.5,则外观可能会有所不同)(也许您必须使用其他运算符):

this.af.getObservable(`userChats/${this.userID}/`).pipe(
  // we map the array of conversations to an array of the (before inner) observables

  map(recentConversations =>
    recentConversations.map(conversation =>
      this.af.getObservableSortByVar(`allChats/${conversation.conversationID}/`, 'lastUpdate'))),

  // combine the observables. will emit a new value of ALL conversation data when one of the conversations changes

  switchMap(recentConversations => combineLatest(recentConversations)),
  // map each conversation to the conversation object (this is the code you had in your inner subscription)
  map(conversations =>
    conversations.map(conversationData => {
      let userKey, lastMessage, lastMessageText, lastMessageSender, lastMessageDate;

      for (let i = 0; conversationData.length > i; i++) {
        switch (conversationData[i].key) {
          case 'messages': {
            lastMessage = (conversationData[i][Object.keys(conversationData[i])[Object.keys(conversationData[i]).length - 1]]);
            lastMessageText = lastMessage.message;
            lastMessageSender = lastMessage.sender;
            lastMessageDate = lastMessage.date;
          } // don't you need a "break;" here?
          case 'users': {
            userKey = conversationData[i].userKey;
          }
        }
      }
      return this.createConversationObject('username', userKey, lastMessageSender, lastMessageText, lastMessageDate);
    }))
).subscribe(recentChats => this.recentChats = recentChats);