Подтвердить что ты не робот

Горячие и холодные наблюдаемые: есть ли "горячие" и "холодные" операторы?

Я рассмотрел следующий вопрос SO: Что такое "Горячие и холодные наблюдаемые" ?

Подводя итог:

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

Тем не менее, я чувствую, что горячий и холодный все еще является источником путаницы. Итак, вот мои вопросы:

  • Все ли rx наблюдаемые холодные по умолчанию (за исключением предметов)?

    Я часто читаю, что события являются типичной метафорой для горячих наблюдаемых, но я также читал, что Rx.fromEvent(input, 'click') является наблюдаемым холодом (?).

  • Существуют ли какие-либо операторы Rx, которые превращают холодные наблюдаемые в горячие наблюдаемые (кроме publish и share)?

    Например, как это работает с оператором Rx withLatestFrom? Пусть cold$ является наблюдаемым холодом, на котором он был где-то подписан. Будет ли sth$.withLatestFrom(cold$,...) быть горячим наблюдаемым?

    Или, если я сделаю sth1$.withLatestFrom(cold$,...), sth2$.withLatestFrom(cold$,...) и подписаться на sth1 и sth2, всегда буду видеть одно и то же значение для sth?

  • Я думал, что Rx.fromEvent создает холодные наблюдаемые, но это не так, как упоминалось в одном из ответов. Тем не менее, я по-прежнему смущен этим поведением: codepen.io/anon/pen/NqQMJR?editors=101. Различные подписки получают разные значения от одного и того же наблюдаемого. Разве не было событие click?

4b9b3361

Ответ 1

Я возвращаюсь несколько месяцев спустя к моему первоначальному вопросу и хотел бы поделиться полученными знаниями в то же время. Я буду использовать следующий код для поддержки объяснений (jsfiddle):

var ta_count = document.getElementById('ta_count');
var ta_result = document.getElementById('ta_result');
var threshold = 3;

function emits ( who, who_ ) {return function ( x ) {
  who.innerHTML = [who.innerHTML, who_ + " emits " + JSON.stringify(x)].join("\n");
};}

var messages$ = Rx.Observable.create(function (observer){
  var count= 0;
  setInterval(function(){
    observer.onNext(++count);
  }, 1000)
})
.do(emits(ta_count, 'count'))
.map(function(count){return count < threshold})
.do(emits(ta_result, 'result'))

messages$.subscribe(function(){});

Как упоминалось в одном из ответов, определение наблюдаемого приводит к серии обратных вызовов и регистрации параметров. Поток данных должен быть введен, и это делается с помощью функции subscribe. A (упрощенный для иллюстрации) подробный поток можно найти после этого.

Упрощенная блок-схема

По умолчанию наблюдатели холодны. Подписывание наблюдаемому приведет к тому, что цепочка подписки вверх по течению будет иметь место. Последняя подписка приводит к выполнению функции, которая будет обрабатывать источник и передавать свои данные наблюдателю.

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

Холодная наблюдаемая упрощенная блок-схема

Горячие наблюдаемые могут быть созданы либо с помощью объекта, либо с помощью оператора multicast (и его производных, см. примечание 3 ниже).

Оператор multicast под капотом использует объект и возвращает видимое соединение. Все подписки на оператора будут подписываться на внутренний предмет. Когда вызывается connect, внутренний субъект подписывается наблюдаемым вверх по течению, а данные идут вниз по течению. Субъекты манипулируют внутренним списком подписчиков и многоадресными входящими данными для всех зарегистрированных наблюдателей.

Следующая диаграмма суммирует ситуацию.

Горячая наблюдаемая упрощенная блок-схема

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

Например, если obs горячий, hotOrCold = obs.op1 холодный или горячий? Каким бы ни был ответ:

  • если нет подписчиков obs.op1, никакие данные не будут проходить через op1. Если есть подписчики на горячие obs, это означает, что obs.op1 может потерять часть данных
  • предположим, что op1 не является многоадресным оператором, дважды подписываясь на hotOrCold, будет подписаться дважды на op1, и каждое значение из obs будет проходить дважды через op1.

Примечания:

  • Эта информация должна быть действительна для Rxjs v4. Версия 5 прошла через значительные изменения, и некоторые операторы/субъекты могут не быть присутствует.
  • Неподписались, потоки ошибок и завершения не представлены, так как они не входят в сферу действия вопроса. Планировщики также не принимая во внимание. Помимо всего прочего, они влияют на сроки поток данных, но априори не его направление и содержание.
  • В зависимости от типа объекта, используемого для многоадресной рассылки, существуют различные производные многоадресные операторы:

Subject type | `Publish` Operator | `Share` operator ------------------ | --------------------------- | ----------------- Rx.Subject | Rx.Observable.publish | share Rx.BehaviorSubject | Rx.Observable.publishValue | shareValue Rx.AsyncSubject | Rx.Observable.publishLast | N/A Rx.ReplaySubject | Rx.Observable.replay | shareReplay

Обновить. См. также следующие статьи здесь и там) по этому вопросу от Ben Lesh.

Более подробную информацию по темам можно найти в этом другом вопросе SO: Какова семантика различных предметов RxJS?

Ответ 2

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

То есть активный (горячий) наблюдаемый будет испускать элементы, подписанные или нет. Канонический пример, опять же, события нажатия кнопки происходят, если кто-то их слушает или нет. Это различие важно, потому что, если, например, я нажимаю кнопку, а затем подписываюсь на нажатия кнопок (в указанном порядке), я не увижу, что нажатие кнопки уже произошло.

Пассивный (холодный) наблюдаемый будет ждать, пока абонент не появится перед испуском элементов. Представьте себе кнопку, в которой вы не можете нажимать на нее, пока кто-то не прослушивает события - это гарантирует, что вы всегда увидите каждое событие клика.

Все ли Rx наблюдаемые "холодные" (или пассивные) по умолчанию? Нет, Rx.fromEvent(input, 'click'), например, является горячим (или активным) наблюдаемым.

Я также читал, что Rx.fromEvent(input, 'click') является наблюдаемым холодом (?)

Это не так.

Существуют ли Rx-операторы, которые превращают холодный наблюдаемый в горячий наблюдаемый?

Концепция превращения горячего (активного) наблюдаемого в холодное (пассивное) наблюдаемое заключается в следующем: вам нужно записывать события, которые происходят, пока ничего не подписывается, и предлагать эти предметы (по-разному) подписчикам, которые входят в будущее. Один из способов сделать это - использовать Subject. Например, вы можете использовать ReplaySubject для буферизации выбранных элементов и повторного воспроизведения их будущим подписчикам.

Два оператора, которые вы назвали (publish и share), используют объекты внутри, чтобы предлагать эту функциональность.

Как это работает с оператором Rx withLatestFrom? Пусть cold$ является наблюдаемым холодом, на который подписаны. Будет ли something$.withLatestFrom(cold$,...) быть горячим наблюдаемым?

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

var clickWith3 = Rx.fromEvent(input, 'click')
    .withLatest(Rx.Observable.from([1, 2, 3]);

Или, если я сделаю foo$.withLatestFrom(cold$,...), bar$.withLatestFrom(cold$,...) и подписаться на foo и bar, всегда буду видеть одинаковые значения для обоих?

Не всегда. Опять же, если foo и bar клики на разных кнопках, например, вы увидите разные значения. Кроме того, даже если они были одной и той же кнопкой, если ваша комбинационная функция (второй аргумент withLatest) не возвращает тот же результат для одних и тех же входов, то вы не увидите одинаковых значений (потому что он будет вызываться дважды, как объяснено ниже).

Я думал, что Rx.fromEvent создает холодные наблюдаемые, но это не так, как упоминалось в одном из ответов. Тем не менее, я по-прежнему смущен этим поведением: codepen.io/anon/pen/NqQMJR?editors=101. Различные подписки получают разные значения от одного и того же наблюдаемого. Разве не было событие click?

Я укажу вам этот отличный ответ Enigmativity на вопрос, который у меня был о том же поведении. Этот ответ объяснит это намного лучше, чем я могу, но суть в том, что источник (событие click) является "общим", да, но ваши операции на нем не являются. Если вы хотите разделить не только событие клика, но и операцию на нем, вам нужно будет сделать это явно.

Ответ 3

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

Вы можете думать о values как о генераторе потоков для click с прикрепленным map.

.share() в конце этой карты создаст поведение, которое мы ожидаем, потому что оно неявно подписывается.

Ответ 4

Это не ответ на все ваши вопросы (я хотел бы знать их все!), но наверняка, все fromEvent Наблюдения горячие. Щелчок, кажется, не потому, что он не является "непрерывным" событием вроде mousemove, но в любом случае подписка на вызов источника (addEventListener или on) выполняется только один раз, когда создается Observable. Так жарко. Вы можете увидеть его в исходном коде оператора здесь и there - created visibleable share d независимо от того, какое имя или источник события.