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

jcr*_*oll 3 rxjs angular-http angular

在下面的代码中,我有一个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);
  }
Run Code Online (Sandbox Code Playgroud)

有没有办法使用 RxJS 使用switchMap(或类似的东西)将这些调用链接在一起,以便在所有后续请求完成之前,observable 不被认为是完整的,而不必编写某种自定义递归逻辑?

max*_*992 5

如果您只想使用 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();
Run Code Online (Sandbox Code Playgroud)

输出是:
在此处输入图片说明

我做了一个 Plunkr,所以你可以看到它的实际效果:https ://plnkr.co/edit/xFWXyAJM6qm0XwMf4tmv?p=preview


这里有趣的部分是我们如何处理调用。
让我给你更多的细节:

from允许我们一个accountIds一个地发送observable :

  .from(user.accountIds)
Run Code Online (Sandbox Code Playgroud)

然后我们可以为accountId我们想要发送到后端的每个对象构建:

  .map(accountId => ({ id: accountId, userIds: [user.id] }))
Run Code Online (Sandbox Code Playgroud)

完成后,我们创建一个Observable,一旦我们订阅它,它将进行 HTTP 调用:

  .map(account => apiService.post(`/accounts/${account.id}`, account))
Run Code Online (Sandbox Code Playgroud)

感谢concatAll,我们有您所期望的行为: 一一进行每个 HTTP 调用:

  .concatAll()
Run Code Online (Sandbox Code Playgroud)

显示 HTTP 调用的响应:

  .do(console.log)
Run Code Online (Sandbox Code Playgroud)

现在,我们想在users每个请求accounts完成后发出最终请求。last将帮助我们等待最后一个(这里是明显的队长):.last() 只需记录每个请求accounts都已完成

  .do(_ => console.log('Finished to chain the HTTP calls for accounts'))
Run Code Online (Sandbox Code Playgroud)

users .switchMap(_ => apiService.post( /users/${user.id}, user))进行最后的 HTTP 调用输出响应

  .do(console.log)
Run Code Online (Sandbox Code Playgroud)

当然,我们需要订阅,因为这是一个冷的 Observable,否则什么都不会发生。* 。订阅();

*:在你的 effect 中,你可能只想返回 Observable,因为 ngrx/effects 会为你订阅它;)