【问题标题】:Flattening nested Observables扁平化嵌套的 Observables
【发布时间】:2017-03-19 15:07:20
【问题描述】:

我被困在嵌套的可观察地狱中,可以用手做。

我有以下代码块

return this.findUser(term).map( users => {
  return users.map( user => this.getLastLogin(user.user_id).map( last_login => {
    user.last_login = last_login;
    return user;
  }));
});

findUser 返回Observable<User[]>getLastLogin 返回Observable<number>

我基本上希望获取一个用户列表,然后用另一个值的信息更新它。

现在上面的代码正在返回<Observable<Observable<User>[]>

我以为我可以用flatMap 替换最初的map,但这会将对象变成<Observable<Observable<User>>

RxJS 文档有点难以解读,所以我不确定 switchforkJoinflatMap 的哪种组合可以满足我的需求。

我希望返回Observable<User[]>。谁能指出我正确的方向?

【问题讨论】:

    标签: javascript angular rxjs observable rxjs5


    【解决方案1】:

    实际上,您不需要forkJoin()switch() 来执行此操作。

    通常,您希望通过另一个异步调用来更新用户数组中的每个用户。

    我会这样做:

    var source = findUser('term')
        .mergeAll()
        .mergeMap(user => getLastLogin(user.user_id)
            .map(last_login => {
                user.last_login = last_login;
                return user;
            })
        )
        .toArray();
    
    source.subscribe(val => console.log(val));
    

    运算符mergeAll() 将高阶 Observable 转换为单个 observable。在这种情况下,它会获取所有用户的数组并一一重新发出它们。然后mergeMap() 发出使用last_login 日期更新的用户。最后,我使用toArray() 将单个用户转换为一个大数组,将它们作为整体发出(如果您想发出单个用户,可以删除此运算符)。

    请注意,当您使用 return users.map(...) 时,您使用的是返回数组的 Array.map(),而不是来自返回 Observable 的 RxJS 的 map()。我认为使用单个对象通常比使用对象数组更容易。

    观看现场演示:https://jsbin.com/naqudun/edit?js,console

    这会打印到控制台:

    [ { name: 'foo',
        user_id: 42,
        last_login: 2016-11-06T10:28:29.314Z },
      { name: 'bar',
        user_id: 21,
        last_login: 2016-11-06T10:28:29.316Z } ]
    

    【讨论】:

    • 我认为你的答案更简洁——尤其是当问题的标题提到扁平化嵌套的 observables 时——但我很好奇数组顺序是否总是与源数组的顺序匹配:使用mergeMap 导致不确定的顺序?顺序是否取决于哪些要合并的 observables 先发出?
    • 这是真的,mergemergeAll 都保证相同的顺序,这可能是也可能不是问题。但是,如果这是一个问题,您可以将 mergeMap 替换为 concatMap 即可。
    • 有趣的是,这段代码在使用 TypeScript 的类时不起作用。编译后,findUsers 方法上的 mergeAll 看起来返回了 User[]。这是我看到的行为的链接。 jsbin.com/wiwayikiye/edit?js,console 有什么想法吗?
    • @user3750194 我从未见过你用来创建User 类的语法。我什至不认为这是一个有效的 TypeScript 语法。看到这个jsbin.com/gikabu/2/edit?js,console
    • 多哈。奇怪的是它工作得很好。出现错误,提示“'User[]' 类型上不存在属性 'mergeMap'”。这可能是一些奇怪的 angular/typeScript 业务,但谁知道呢?有什么想法吗?
    【解决方案2】:

    一种解决方案是使用forkJoin 加入,然后将调用getLastLogin 的结果映射给用户:

    // forkJoin will return Observable<User[]>, so use switchMap.
    
    return this.findUser(term).switchMap(users => Observable.forkJoin(
    
      // Map the array of users to the array of observables to be joined. Use
      // first to ensure the observables complete.
    
      users.map(user => this.getLastLogin(user.user_id).first()),
    
      // Use forkJoin's selector to add the last_login to each user and return
      // the users.
    
      (...logins) => {
        users.forEach((user, index) => { user.last_login = logins[index]; });
        return users;
      }
    ));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-12
      • 2019-04-24
      • 1970-01-01
      • 1970-01-01
      • 2017-11-27
      • 2017-09-18
      相关资源
      最近更新 更多