Горячие и холодные наблюдаемые: есть ли "горячие" и "холодные" операторы?
Я рассмотрел следующий вопрос: " Что такое горячая и холодная наблюдаемые?
Подвести итоги:
- холодная наблюдаемая испускает свои значения, когда у нее есть наблюдатель, чтобы потреблять их, то есть последовательность значений, полученных наблюдателями, не зависит от времени подписки. Все наблюдатели будут использовать одну и ту же последовательность значений.
- горячая наблюдаемая выдает значение независимо от своих подписок, то есть значения, полученные наблюдателями, являются функцией времени подписки.
Тем не менее, я чувствую, что жарко против холода все еще является источником путаницы. Итак, вот мои вопросы:
Все ли наблюдаемые 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
создает холодные наблюдаемые, но это не так, как упоминалось в одном из ответов. Тем не менее, я все еще озадачен этим поведением: http://Codepen%20is%20here%20:%20codepen.io/anon/pen/NqQMJR?editors=101. Разные подписки получают разные значения из одной и той же наблюдаемой. Не былclick
событие поделился?
4 ответа
Я возвращаюсь через несколько месяцев к своему первоначальному вопросу и хочу поделиться тем временем полученными знаниями. Я буду использовать следующий код в качестве поддержки объяснения ( 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
функция. Подробное описание (упрощенное для иллюстрации) можно найти после этого.
Наблюдаемые значения являются холодными по умолчанию. Подписка на наблюдаемое приведет к созданию восходящей цепочки подписок. Последняя подписка приводит к выполнению функции, которая будет обрабатывать источник и передавать его данные наблюдателю.
Этот наблюдатель, в свою очередь, передает следующему наблюдателю, что приводит к потоку данных вниз по течению вниз к наблюдателю-получателю. Следующая упрощенная иллюстрация показывает подписку и потоки данных, когда два подписчика подписываются на одну и ту же наблюдаемую.
Горячие наблюдаемые могут быть созданы либо с помощью предмета, либо через 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
Обновление: см. Также следующие статьи, здесь и там) на эту тему Бен Леш.
Более подробную информацию о предметах можно найти в этом другом вопросе SO: Какова семантика разных предметов RxJS?
Ваше резюме и связанный вопрос верны, я думаю, что терминология может сбить вас с толку. Я предлагаю вам рассматривать горячие и холодные наблюдаемые как активные и пассивные наблюдаемые (соответственно).
То есть активная (горячая) наблюдаемая будет излучать элементы независимо от того, подписан кто-то или нет. Канонический пример, опять же, события нажатия кнопки происходят независимо от того, слушает их кто-то или нет. Это различие важно, потому что, если, например, я нажму кнопку, а затем подпишусь на нажатие кнопки (в таком порядке), я не увижу нажатие кнопки, которое уже произошло.
Пассивная (холодная) наблюдаемая будет ждать, пока не появится подписчик, прежде чем испускать предметы. Представьте себе кнопку, на которой вы не можете нажать на нее, пока кто-то не прослушает события - это гарантирует, что вы всегда будете видеть каждое нажатие.
Являются ли все наблюдаемые Rx "холодными" (или пассивными) по умолчанию? Нет, Rx.fromEvent(input, 'click')
например, горячая (или активная) наблюдаемая.
Я также читал, что
Rx.fromEvent(input, 'click')
холодная наблюдаемая (?)
Это не относится к делу.
Существуют ли Rx-операторы, которые превращают наблюдаемую холодную в наблюдаемую горячую?
Концепция превращения горячей (активной) наблюдаемой в холодную (пассивную) наблюдаемую заключается в следующем: вам нужно записывать события, которые происходят, когда ничего не подписано, и предлагать эти элементы (различными способами) подписчикам, которые появятся в будущем. Один из способов сделать это - использовать тему. Например, вы можете использовать ReplaySubject
буферизовать отправленные элементы и воспроизводить их для будущих подписчиков.
Два оператора вы назвали (publish
а также share
) оба используют предметы внутри, чтобы предложить эту функциональность.
Как это работает с оператором Rx
withLatestFrom
? Позволятьcold$
быть холодной наблюдаемой, на которую был подписан. Будетsomething$.withLatestFrom(cold$,...)
быть горячей наблюдаемой?
Если something
это горячая наблюдаемая, тогда да. Если something
холодная наблюдаемая, то нет. Возвращаясь к примеру с событиями, если something
поток событий нажатия кнопки:
var clickWith3 = Rx.fromEvent(input, 'click')
.withLatest(Rx.Observable.from([1, 2, 3]);
Или если я сделаю
foo$.withLatestFrom(cold$,...), bar$.withLatestFrom(cold$,...)
и подписаться наfoo
а такжеbar
я всегда буду видеть одинаковые значения для обоих?
Не всегда. Опять же, если foo
а также bar
Например, если нажать на разные кнопки, вы увидите разные значения. Кроме того, даже если они были одной и той же кнопкой, если ваша комбинационная функция (2-й аргумент withLatest
) не возвращает один и тот же результат для одних и тех же входных данных, тогда вы не увидите одинаковые значения (потому что он будет вызван дважды, как описано ниже).
я думал
Rx.fromEvent
создает холодные наблюдаемые, но это не так, как упоминалось в одном из ответов. Тем не менее, я все еще озадачен этим поведением: http://codepen.io/anon/pen/NqQMJR?editors=101. Разные подписки получают разные значения из одной и той же наблюдаемой. Не былclick
событие поделился?
Я укажу вам этот замечательный ответ от Enigmativity на вопрос, который у меня был о том же поведении. Этот ответ объяснит это намного лучше, чем я, но суть в том, что источник (событие click) является "общим", да, но ваши операции с ним не выполняются. Если вы хотите поделиться не только событием click, но и операцией над ним, вам нужно будет сделать это явно.
values
в вашем коде это лениво - ничего не происходит, пока что-то не подпишется, и в этот момент оно проходит и подключается. Итак, в вашем примере, хотя вы подписываетесь на одну и ту же переменную, она создает два разных потока; по одному на каждый звонок подписки.
Вы можете думать о values
как генератор потоков для click
с этим map
прилагается.
.share()
в конце этой карты будет создано поведение, которое мы ожидаем, потому что оно неявно подписывается.
Это не ответ на все ваши вопросы (я хотел бы знать их все!), Но наверняка, все fromEvent
Наблюдаемые горячие. Кажется, что щелчок происходит не потому, что это не "непрерывное" событие, как mousemove, а в любом случае подписка на источник (addEventListener
или же on
вызов) выполняется только один раз, когда создается Observable. Так что жарко. Вы можете увидеть это в исходном коде оператора тут и там - созданная наблюдаемая share
не имеет значения, как называется событие или источник.