2016-11-06 255 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; 
    } 
));