有没有办法使用RxJS将n个http请求链接在一起?

时间:2017-05-23 21:19:08

标签: angular rxjs angular-http

在下面的代码中,我有一个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不被视为完整 而不必写一些自定义递归逻辑?

3 个答案:

答案 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();

输出结果为:
enter image description here

我做了一个普兰克,所以你可以看到它在行动:
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

From the docs:

  

concat将多个Observable连接在一起,一次订阅一个Observable并将其结果合并到输出Observable中。您可以传递一个Observable数组,也可以直接将它们作为参数。传递一个空数组将导致Observable立即完成。

     

concat将订阅第一个输入Observable并发出它的所有内容   价值观,不以任何方式改变或影响它们。当那个   Observable完成后,它会订阅然后下一个Observable传递   再次,发出它的价值观。这将重复,直到操作员   耗尽了Observables。当最后一个输入Observable完成时,concat   也将完成。在任何特定时刻,只有一个Observable通过   运算符发出值。如果你想从传递中发出值   Observables同时,检查合并,尤其是与   可选的并发参数。事实上,concat是一个   等效并发参数设置为1的合并运算符。