RxSwift combineLatest генерирует все воспроизводимые элементы из последнего наблюдаемого

#rx-swift #combinelatest

#rx-swift #combinelatest

Вопрос:

Я вижу некоторые неожиданные результаты при использовании combineLatest для коллекции холодных наблюдаемых. Он генерирует последние из всех, кроме последнего наблюдаемого, и вместо этого объединяет последние из первых (n-1) наблюдаемых с каждым элементом из n-го наблюдаемого.

 let observable = ReplaySubject<Int>.createUnbounded()
let observable2 = ReplaySubject<String>.createUnbounded()

observable.onNext(1)
observable.onNext(2)
observable.onNext(3)
observable.onNext(4)

observable2.onNext("bed")
observable2.onNext("book")
observable2.onNext("table")

let latestObserver = Observable.combineLatest(observable, observable2)

_ = latestObserver
    .subscribe(onNext: {
    print($0)
})
.disposed(by: disposeBag)
  

Выдает вывод:
(4, «кровать»)
(4, «книга»)
(4, «таблица»)

Я ожидал увидеть вывод только (4, «таблица»).

Если я изменю порядок наблюдаемых следующим образом:

 let latestObserver = Observable.combineLatest(observable2, observable)
  

Я получаю вывод:
(«таблица», 1)
(«таблица», 2)
(«таблица», 3)
(«таблица», 4)

Если я добавлю окончательный произвольный наблюдаемый, то я увижу только последний из каждого из первых:

 let observable = ReplaySubject<Int>.createUnbounded()
let observable2 = ReplaySubject<String>.createUnbounded()
let observable3 = Observable<Int>.just(42)

observable.onNext(1)
observable.onNext(2)
observable.onNext(3)
observable.onNext(4)

observable2.onNext("bed")
observable2.onNext("book")
observable2.onNext("table")

let latestObserver = Observable.combineLatest(observable, observable2, observable3)

_ = latestObserver
    .subscribe(onNext: {
    print($0)
})
.disposed(by: disposeBag)
  

выдает вывод: (4, «таблица», 42)

Действительно ли это ожидаемое поведение?

Комментарии:

1. Исходя из этого вопроса и вашего последнего, у меня создается впечатление, что у вас возникли проблемы с отслеживанием того, какие наблюдаемые являются синхронными, а какие асинхронными…

2. @DanielT. Действительно. Не вдаваясь в подробности, это вытекает из спецификации протокола модели в качестве входных данных для контроллера представления. Протокол определяет наблюдаемые объекты, в которых я ранее привык видеть BehaviorRelay или BehaviourSubject . Контроллер представления должен обновлять свой дисплей и элементы управления только из последних значений и игнорировать любые промежуточные воспроизводимые значения. Просто пытаюсь понять последствия различных возможных будущих конкретных реализаций протокола, которые могут быть вне моего контроля. Я думал, что понял вещи, но обнаружил некоторые сюрпризы. Спасибо

3. Ах, я понимаю… В любом случае вам следует избегать использования subjects (включая ReplaySubject .) Возможно, это поможет вам избежать выделения всех этих «промежуточных значений воспроизведения» в первую очередь. Объекты следует использовать только для преобразования кода, отличного от Rx, в Rx и для обработки циклов зависимостей (где выходные данные наблюдаемого могут влиять на его собственные входные данные).

Ответ №1:

Давайте разберем, что происходит в вашем первом примере…

Вы могли бы использовать Observable.from вместо subject и получить те же результаты… Шаги в коде следующие,

  1. создайте два ReplaySubject s и загрузите их событиями.
  2. combineLatest Оператор подписывается на первый объект.
  3. Этот первый объект немедленно воспроизводит все свои значения.
  4. Поскольку второй объект еще не подписан, combineLatest оператор ничего не генерирует, вместо этого он молча поглощает значения, всегда сохраняя «последнее».
  5. Затем combineLatest оператор подписывается на второй объект.
  6. При этом воспроизводятся все его значения.
  7. Поскольку combineLatest оператор теперь получил следующее событие от каждого из своих источников, он генерирует значения, переданные из второго источника, в сочетании с последним (т. Е. Последним) из первого источника.

Ваши объекты воспроизведения по своей сути синхронны. Они передают все значения подписчику сразу после подписки.

В вашем последнем примере кода, поскольку последнее наблюдаемое из трех выдает только одно значение и только после того, как два других выдали все свои значения, вы видите только один вывод, содержащий последнее из двух предыдущих наблюдаемых.