TL; DR: Вам нужно HOT наблюдаемое, когда вы не хотите создавать своего продюсера снова и снова.

ХОЛОДНЫЙ - это когда ваше наблюдаемое создает производителя

// COLD
var cold = new Observable((observer) => {
  var producer = new Producer();
  // have observer listen to producer here
});

ГОРЯЧИЙ - это когда ваша наблюдаемая закрывается над производителем

// HOT
var producer = new Producer();
var hot = new Observable((observer) => {
  // have observer listen to producer here
});

Углубляясь в происходящее…

Моя последняя статья о Изучение наблюдаемых посредством построения наблюдаемых была в основном для того, чтобы проиллюстрировать, что наблюдаемые - это просто функции. Цель заключалась в том, чтобы демистифицировать сами наблюдаемые, но на самом деле она не затрагивает одну из проблем, которые, кажется, больше всего сбивают с толку людей в отношении наблюдаемых: понятие горячее и холодное.

Наблюдаемые - это просто функции!

Observables - это функции, которые связывают наблюдателя с производителем. Вот и все. Они не обязательно настраивают продюсера, они просто настраивают наблюдателя, который слушает продюсера, и обычно возвращают механизм удаления для удаления этого слушателя. Акт подписки - это акт «вызова» наблюдаемого как функции и передачи ему наблюдателя.

Что такое «продюсер»?

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

Cold Observables: Производители созданы * внутри *

Наблюдаемый объект является «холодным», если его основной производитель создан и активирован во время подписки. Это означает, что если наблюдаемые объекты являются функциями, то производитель создается и активируется посредством вызова этой функции.

  1. создает продюсер
  2. активирует производителя
  3. начинает слушать продюсера
  4. одноадресная передача

Пример ниже является «холодным», потому что он создает и прослушивает WebSocket внутри функции подписчика, которая вызывается при подписке на Observable:

const source = new Observable((observer) => {
  const socket = new WebSocket('ws://someurl');
  socket.addEventListener('message', (e) => observer.next(e));
  return () => socket.close();
});

Таким образом, все, что подписывается на `source` выше, получит свой собственный экземпляр WebSocket, и когда он откажется от подписки, он будет` close () `этот сокет. Это означает, что наш источник действительно только одноадресный, потому что производитель может отправлять только одному наблюдателю. Вот базовый JSBin, иллюстрирующий идею.

Горячие наблюдаемые: производители созданы * за пределами *

Наблюдаемое является «горячим», если его основной производитель либо создан, либо активирован вне подписки.

  1. делится ссылкой на производителя
  2. начинает слушать продюсера
  3. многоадресная рассылка (обычно²)

Если бы мы возьмем наш пример выше и переместим создание WebSocket за пределы нашего наблюдаемого объекта, он станет «горячим»:

const socket = new WebSocket('ws://someurl');
const source = new Observable((observer) => {
  socket.addEventListener('message', (e) => observer.next(e));
});

Теперь все, что подписывается на `source`, будет использовать один и тот же экземпляр WebSocket. Теперь он будет эффективно рассылать многоадресную рассылку всем подписчикам. Но у нас есть небольшая проблема: у нас больше нет логики для разрыва сокета с помощью нашего наблюдаемого объекта. Это означает, что такие вещи, как ошибки и завершения, а также отказ от подписки больше не будут закрывать для нас сокет. Итак, что мы действительно хотим, так это сделать нашу холодную наблюдаемую горячей. Вот JSBin, показывающий эту основную концепцию.

Зачем делать «горячую» наблюдаемую?

Из первого примера выше, показывающего холодное наблюдаемое, вы можете видеть, что могут быть некоторые проблемы с постоянным наличием всех холодных наблюдаемых. Во-первых, если вы подписывались на наблюдаемый объект более одного раза, который создает какой-то дефицитный ресурс, такой как соединение через веб-сокет, вы не хотите создавать это соединение через веб-сокет снова и снова. На самом деле очень легко создать более одной подписки на наблюдаемое, даже не осознавая этого. Допустим, вы хотите отфильтровать все «нечетные» и «четные» значения из своей подписки на веб-сокет. В следующем сценарии вы создадите две подписки:

source.filter(x => x % 2 === 0)
  .subscribe(x => console.log('even', x));
source.filter(x => x % 2 === 1)
  .subscribe(x => console.log('odd', x));

Предметы Rx

Прежде чем мы сможем сделать нашу «холодную» наблюдаемую «горячей», нам нужно ввести новый тип: Rx Subject. У него есть несколько свойств:

  1. Это наблюдаемое. Он имеет форму наблюдаемого и имеет те же операторы.
  2. Это наблюдатель. Он похож на уток как наблюдатель. При подписке на наблюдаемый объект будет выдавать любое значение, которое вы «следующий» в него в качестве наблюдателя.
  3. Это многоадресная рассылка. Все наблюдатели, переданные ему через `subscribe ()`, добавляются во внутренний список наблюдателей.
  4. Когда это будет сделано, это будет сделано. Субъекты не могут быть повторно использованы после отмены подписки, заполнения или ошибки.
  5. Он передает ценности через себя. Перефразирую № 2, на самом деле. Если вы добавите в него следующее значение, оно выйдет за пределы наблюдаемой части самого себя.

Субъект Rx называется субъектом пункта 3 выше. Субъекты в паттерне Банда четырех наблюдателей - это, как правило, классы с методом addObserver. В этом случае наш метод addObserver - это subscribe. Вот JSBin, показывающий основное поведение Rx Subject.

Сделать холодное наблюдаемым горячим

Вооружившись нашей Rx Subject выше, мы можем использовать немного функционального программирования, чтобы сделать любую «холодную» наблюдаемую «горячей»:

function makeHot(cold) {
  const subject = new Subject();
  cold.subscribe(subject);
  return new Observable((observer) => subject.subscribe(observer));
}

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

Однако у нас все еще есть небольшая проблема: мы не отслеживаем нашу подписку на источник, так как же мы можем удалить ее, когда захотим? Мы можем добавить к нему подсчет ссылок, чтобы решить эту проблему:

function makeHotRefCounted(cold) {
  const subject = new Subject();
  const mainSub = cold.subscribe(subject);
  let refs = 0;
  return new Observable((observer) => {
    refs++;
    let sub = subject.subscribe(observer);
    return () => {
      refs--;
      if (refs === 0) mainSub.unsubscribe();
      sub.unsubscribe();
    };
  });
}

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

В RxJS используйте `publish ()` или `share ()`

Вероятно, вам не следует использовать какие-либо из приведенных выше функций `makeHot`, вместо этого следует использовать такие операторы, как` publish () `и` share () `. Есть много способов и средств сделать холод наблюдаемым горячим, и в Rx есть эффективные и краткие способы выполнения каждой из этих вещей. Можно было бы написать целую статью только о различных операторах, используемых для этого в Rx, но это не было целью здесь. Цель состояла в том, чтобы укрепить представление о том, что на самом деле означает «горячо» и «холодно».

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

Вот JSBin, демонстрирующий использование` share () `для активизации исходного кода в RxJS 5 и показывающий, что его можно повторить.

«Теплая» наблюдаемая

Учитывая все вышеизложенное, можно увидеть, как Observable, будучи просто функцией, на самом деле может быть как «горячим», так и «холодным». Может быть, он наблюдает за двумя продюсерами? Один он создает, а другой закрывается? Это, вероятно, плохая джуджу, но в редких случаях это может быть необходимо. Например, мультиплексированный веб-сокет должен совместно использовать сокет, но отправлять собственную подписку и отфильтровывать поток данных.

«Горячие» и «холодные» - все о продюсере

Если вы закрываете общую ссылку на продюсера в наблюдаемом, это «жарко», если вы создаете нового продюсера в наблюдаемом, это «холодно». Если вы делаете и то, и другое ... что вы делаете? Думаю, он «теплый».

ПРИМЕЧАНИЯ

¹ (ПРИМЕЧАНИЕ: как-то странно говорить, что производитель «активирован» внутри подписки, но не «создается» до некоторого более позднего момента, но с помощью прокси, это может быть возможно.) Обычно для «горячих» наблюдаемых производителей создаются оба производителя. и активируется вне подписки.

² Горячие наблюдаемые обычно являются многоадресными, но они могут прослушивать производителя, который поддерживает только одного слушателя за раз. Основания для того, чтобы называть это «многоадресной рассылкой», в то время немного нечеткие.

Хотите узнать больше? Я провожу семинары по RxJS на RxWorkshop.com!