2016-11-06 86 views
5

我是一个嵌套的可观察地狱,可以用一只手做。拼合嵌套的Observables

我有下面的代码块

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文档有点难以破译,所以我不确定什么组合switch,forkJoinflatMap将使我得到我所需要的。我想寄回Observable<User[]>。任何人都可以将我指向正确的方向吗?

回答

4

其实,你并不需要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()转换高阶可观察到单个观测。在这种情况下,它需要所有用户的数组并逐个重新发射它们。然后mergeMap()发出更新日期为last_login的用户。最后,我使用toArray()将单个用户转换为一个大数组,并将它们作为整体发送(如果您想发出单个用户,则可以删除此运算符)。

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

见现场演示: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 } ] 
+0

我认为你是更合适的答案 - 尤其是作为问题标题中提到扁平化嵌套观测 - 但我很好奇是否数组顺序始终与源数组的顺序相匹配:使用mergeMap会导致非确定性顺序吗?订单是否取决于哪些被合并的可观察量首先发出? – cartant

+1

这是真的,无论是'merge'还是'mergeAll'都可以保证相同的顺序,这可能是也可能不是问题。然而,如果这是一个问题,你可以用'concatMap'替换'mergeMap',就是这样。 – martin

+1

有趣的是,使用TypeScript的类时,此代码不起作用。编译时,它看起来像'findUsers'方法上的'mergeAll'返回'User []'。这是我看到的行为的链接。 https://jsbin.com/wiwayikiye/edit?js,console任何想法? – user3750194

1

一种解决方案是使用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; 
    } 
));