我正在使用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));
});
});
});
当前,我正在调用数据库以检索用户所有对话的列表。
我收到了我所订阅的所有会话的Observable对象,因为我想保持数据库中数据的最新状态。
然后,我遍历对话的“可观察”。我需要为每个迭代的元素(每次对话)进行新的数据库调用,以获得有关它的信息/元数据(内容,senderID,对话日期等)。因此,我得到了两个Observable-一个嵌套在另一个中,两个都已订阅。
当我一次执行整个代码块(程序开始时的初始调用)时,这完成了工作。但是,当数据库中的数据被修改(数据库中的“ userChat”节点或“ allChats”节点,或两者都被激活)并且订阅被激活时,我得到了整个代码块的多个(重复)调用,用相同的结果多次淹没我的数组。
当我只想打一个电话刷新信息时,我会接到不必要的电话。
因此,我可以看到我对Observables的逻辑和理解不正确。有人可以解释上面这个例子的正确解决方案吗?如何在没有重复(相同)调用的情况下嵌套Observable订阅?
答案 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);