2016-11-06 3 views
5

Я застрял во вложенном наблюдаемом аду и мог сделать с рукой.Сглаживание вложенных наблюдаемых

У меня есть следующий блок кода

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>[]>.

Я думал, что смогу заменить начальный map на flatMap, но это превратит объект в <Observable<Observable<User>>.

Документацию RxJS немного сложно расшифровать, поэтому я не уверен, какая комбинация switch, forkJoin или flatMap приведет меня к тому, что мне нужно.

Я надеюсь вернуть 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() Оператор преобразует высший порядок Observable в отдельные наблюдаемые. В этом случае он принимает массив всех пользователей и переизбирает их один за другим. Затем mergeMap() испускает пользователей, обновляемых датой last_login. В конце я использовал toArray(), чтобы преобразовать одиночных пользователей в один большой массив, который их испускает в целом (вы можете удалить этот оператор, если хотите вместо этого выпустить одиночных пользователей).

Обратите внимание, что при использовании return users.map(...) вы использовали Array.map(), который возвращает массив, а не map() из RxJS, который возвращает наблюдаемый. Я думаю, что работать с одиночными объектами обычно проще, чем с массивами объектов.

Смотреть демо: 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' гарантирует тот же порядок, который может или не может быть проблемой.Однако, если это проблема, вы можете просто заменить 'mergeMap' на' concatMap' и все. – martin

+1

Интересно, что этот код не работает при использовании классов TypeScript. При компиляции он выглядит так: 'mergeAll' в методе' findUsers' возвращает '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; 
    } 
)); 
Смежные вопросы