在下面的代码中,我有一个User
对象,该对象可以拥有与之关联的可变数量的帐户ID。当用户更新时,应该为用户对象中包含的每个帐户提出额外的请求:
class User {
id: number;
accountIds: Array<number>;
}
// Side effects class
@Effect()
update$ = this.actions$
.ofType('USER_UPDATE')
.switchMap(action => {
let user = action.payload;
for (let accountId of user.accountIds) {
let account = { id: accountId, userIds: [user.id] };
// Is there any way to chain these api calls with switch maps
// and then finally switch map to the user request below?
return this.apiService.post(`/accounts/${account.id}`, account);
}
return this.apiService.post(`/users/${userid}`, user);
}
有没有办法使用RxJS使用switchMap
(或类似的东西)将这些调用链接在一起,以便在所有后续请求完成之后,observable不被视为完整 而不必写一些自定义递归逻辑?
答案 0 :(得分:3)
如果你只想用Observable来处理它(这在效果中似乎是一个好主意),那么你可以这样做:
const { Observable } = Rx;
// DATA
const user = {
id: 'currentUserId',
accountIds: [
'accountId0',
'accountId1',
'accountId2',
'accountId3'
]
}
// MOCK BACKEND CALLS
const apiService = {
post: (url, data) => {
return Observable.of(`some response for ${url}`).delay(1000);
}
};
// HANDLE THE MULTIPLE HTTP CALLS
Observable
.from(user.accountIds)
.map(accountId => ({ id: accountId, userIds: [user.id] }))
.map(account => apiService.post(`/accounts/${account.id}`, account))
.concatAll()
.do(console.log)
.last()
.do(_ => console.log('Finished to chain the HTTP calls for accounts'))
.switchMap(_ => apiService.post(`/users/${user.id}`, user))
.do(console.log)
.subscribe();
我做了一个普兰克,所以你可以看到它在行动:
https://plnkr.co/edit/xFWXyAJM6qm0XwMf4tmv?p=preview
这里有趣的部分是我们如何处理这些电话 让我再详细介绍一下:
from
允许我们逐一向{ob>发送每个accountIds
:
.from(user.accountIds)
然后我们可以构建我们想要发送到后端的每个accountId
对象:
.map(accountId => ({ id: accountId, userIds: [user.id] }))
一旦完成,我们创建了一个冷 Observable,一旦我们订阅它就会进行HTTP调用:
.map(account => apiService.post(`/accounts/${account.id}`, account))
感谢concatAll
,我们有您期望的行为:逐个进行每次HTTP调用:
.concatAll()
显示HTTP呼叫的响应:
.do(console.log)
现在,我们要在完成对users
的每个请求后向accounts
发出最终请求。 last
将帮助我们等待最后一个(队长明显在这里):
。持续()
只需记录每个accounts
的请求都已完成
.do(_ => console.log('Finished to chain the HTTP calls for accounts'))
对users
进行最后的HTTP调用
.switchMap(_ =&gt; apiService.post(/users/${user.id}
,user))
输出回复
.do(console.log)
当然我们需要订阅,因为这是一个冷的Observable,否则什么都不会发生。 * .subscribe();
*:在您的效果中,您可能只想返回Observable,因为ngrx / effects会为您订阅它;)
答案 1 :(得分:2)
试试这个:
update$ = this.actions$
.ofType('USER_UPDATE')
.pluck('payload')
.switchMap(user =>
Observable.from(user.accountIds)
.concatMap(accountId =>
this.apiService.post(`/accounts/${accountId}`, {id: accountId, userIds: [user.id]})
)
.concat(Observable.defer(() => this.apiService.post(`/users/${user.id}`, user)))
);
以下是硬编码数据的示例: https://jsfiddle.net/96x907ae/1/
答案 2 :(得分:-2)
我认为您正在寻找Observable.concat
。
concat将多个Observable连接在一起,一次订阅一个Observable并将其结果合并到输出Observable中。您可以传递一个Observable数组,也可以直接将它们作为参数。传递一个空数组将导致Observable立即完成。
concat将订阅第一个输入Observable并发出它的所有内容 价值观,不以任何方式改变或影响它们。当那个 Observable完成后,它会订阅然后下一个Observable传递 再次,发出它的价值观。这将重复,直到操作员 耗尽了Observables。当最后一个输入Observable完成时,concat 也将完成。在任何特定时刻,只有一个Observable通过 运算符发出值。如果你想从传递中发出值 Observables同时,检查合并,尤其是与 可选的并发参数。事实上,concat是一个 等效并发参数设置为1的合并运算符。