2016-11-06 6 views
5

Ich bin in der verschachtelten beobachtbaren Hölle fest und könnte mit einer Hand tun.Abflachung verschachtelte Observables

Ich habe den folgenden Code-Block

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 kehrt Observable<User[]> und getLastLogin kehrt Observable<number>.

Ich hoffe grundsätzlich, eine Liste von Benutzern zu holen und dann diese mit den Informationen von einem anderen Wert zu aktualisieren.

Im Moment gibt der obige Code <Observable<Observable<User>[]> zurück.

Ich dachte, ich könnte die ursprüngliche map durch flatMap ersetzen, aber dies verwandelt das Objekt in <Observable<Observable<User>>.

Die RxJS-Dokumentation ist ein wenig schwer zu entschlüsseln, also bin ich mir nicht sicher, welche Kombination von switch, forkJoin oder flatMap mich dazu bringen wird, was ich brauche.

Ich hoffe, Observable<User[]> zurückzukehren. Kann mir jemand in die richtige Richtung zeigen?

Antwort

4

Eigentlich brauchen Sie nicht forkJoin() noch switch() dies zu tun.

Im Allgemeinen möchten Sie jeden Benutzer im Array von Benutzern durch einen anderen Async-Aufruf aktualisieren.

ich es so tun würde:

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)); 

Operator mergeAll() wandelt eine höhere Ordnung beobachtbare in einzelne Observablen. In diesem Fall nimmt es das Array aller Benutzer und sendet sie einzeln nacheinander aus. Dann sendet mergeMap() Benutzer mit dem Datum last_login aktualisiert. Am Ende habe ich toArray() verwendet, um einzelne Benutzer in ein großes Array zu transformieren, das als Ganzes ausgegeben wird (Sie können diesen Operator entfernen, wenn Sie stattdessen einzelne Benutzer ausgeben möchten).

Hinweis: Wenn Sie return users.map(...) verwendet haben, haben Sie Array.map() verwendet, das ein Array und nicht map() von RxJS zurückgibt, das ein Observable zurückgibt. Ich denke, das Arbeiten mit einzelnen Objekten ist in der Regel einfacher als mit Arrays von Objekten.

Siehe Live-Demo: https://jsbin.com/naqudun/edit?js,console

Diese Drucke zu trösten:

[ { 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

Ich denke, Ihnen ist die sauberere Antwort - zumal der Titel der Frage erwähnt verschachtelte Observablen Abflachung - aber ich bin neugierig, ob Die Array-Reihenfolge würde immer mit der des Quell-Arrays übereinstimmen: Würde die Verwendung von 'mergeMap' zu einer nicht-deterministischen Reihenfolge führen? Würde die Reihenfolge davon abhängen, welche zu verschmelzenden Observablen zuerst emittieren? – cartant

+1

Dies ist wahr, entweder "merge" oder "mergeAll" garantiert die gleiche Reihenfolge, die ein Problem sein kann oder nicht.Wenn es jedoch ein Problem ist, könntest du 'mergeMap' durch' concatMap' ersetzen und das war's. – martin

+1

Interessanterweise funktioniert dieser Code nicht, wenn Sie TypeScript-Klassen verwenden. Wenn es kompiliert wird, sieht es so aus, als ob 'mergeAll' in der 'findUsers'-Methode einen' User [] 'zurückgibt. Hier ist ein Link zu dem Verhalten, das ich sehe. https://jsbin.com/wiwayikiye/edit?js,console Irgendwelche Ideen? – user3750194

1

Eine Lösung wäre forkJoin zu verwenden zu verbinden und ordnen Sie die Ergebnisse der Anrufe an getLastLogin an Benutzer:

// 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; 
    } 
));