Паттерн Наблюдатель:

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

Наблюдаемое:

Мне нравится думать о Observable как о функции, которая «выбрасывает» значения. Он может «выдавать» значения синхронно или асинхронно. Если вас интересуют эти значения, вы можете зарегистрировать наблюдателя.

Наблюдатель:

Наблюдатель - это объект с тремя функциями.

  1. next () = ›Observable, пожалуйста, вызовите эту функцию, когда у вас будет новое значение для меня.
  2. error () = ›Observable, вызовите эту функцию, когда у вас появится новая ошибка.
  3. complete () = ›Observable, пожалуйста, вызовите эту функцию после завершения работы.

Когда Observable (то есть функция) «выбрасывает» новое значение, ошибку или завершается, он вызывает соответствующую функцию вашего наблюдателя.

Толчок против тяги:

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

var it = makeIterator(['yo', 'ya']);
console.log(it.next().value); // 'yo'

С Observable это похоже на то, что не звоните нам, мы звоним вам.

Наблюдаемый - босс. Когда у него появится новое значение, он подтолкнет это значение к вам. Ваша работа - просто «слушать».

Метафора реального мира - информационные письма:

Как информационный бюллетень появляется в вашем электронном письме? Вы подписываетесь на информационный бюллетень; когда появляется новый информационный бюллетень, менеджер просто отправляет его на вашу электронную почту.

Хватит речей, давайте начнем пачкать руки и разберемся, как работает Observable, создав наш простой мини-приемник.

Нам нужно, чтобы этот код работал:

Давайте начнем.

Мы начинаем с настройки класса Observable и сохраняем ссылку на функцию, которая «выбрасывает» значения. Теперь вы можете понять, почему Observables ленивы; мы не вызываем функцию, пока просто сохраняем ссылку.

Функция подписки:

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

Поздравляю! Вы создали свой новый Observable.

Давайте посмотрим, как мы можем создать метод карты.

fakeAsyncData$.map(val => `New value ${val}`).subscribe({
   next(val) { console.log(val) } ,
   error(e) { console.log(e) } ,
   complete() { console.log(‘complete’) } 
});

Когда мы вызываем map, происходит то, что метод map возвращает новый Observable, который подписывается на источник, в нашем случае fakeAsyncData $. Когда источник сначала «бросает» новое значение, он попадает в метод карты, а после применения функции проекции к значению, карта Observable «бросает» значение на нас. (помните, что мы подписаны на карту Observable)

Давайте посмотрим, насколько легко теперь создать метод fromEvent.

var button = document.getElementById(‘button’);
let clicks$ = Observable.fromEvent(button, 'click')
              .map(e => `${e.pageX}px`);
let unsubscribe = clicks$.subscribe({
  next(val) { console.log(val) } ,
  error(e) { console.log(e) } ,
  complete() { console.log('complete') } 
});

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

setTimeout(() => unsubscribe(), 1000);

Давайте реализуем метод fromArray:

let array$ = Observable.fromArray([1,2,3]);
array$.subscribe({
 next(val) { console.log(val) } ,
 error(e) { console.log(e) } ,
 complete() { console.log(‘complete’) } 
});

Мы видим, что Observables тоже могут быть синхронными.

Понимание mergeMap:

Допустим, нам нужно сделать следующее:

let promise = val => {
  return new Promise(resolve => {
    setTimeout(() => resolve(val), 3000);
 });
}
let data$ = Observable.fromArray([1,2,3]).map(val =>  Observable.fromPromise(promise(val)));

После запуска этого кода мы получим Observable, потому что функция map возвращает Observable. Что нам нужно, так это способ объединить этот Observable с потоком. Вот почему он называется mergeMap; мы выполняем и операцию сопоставления, и операцию слияния одновременно.

Давайте создадим простой метод mergeMap:

Я уверен, что теперь вы сами понимаете, что мы здесь делаем. Теперь вы добавили Observable в поток.

let data$ = Observable.fromArray([1,2,3]).
            mergeMap(val => Observable.fromPromise(promise(val)));

Последний совет:

Если вам нужно использовать только следующую функцию или вам не нравится способ объекта, вы можете сделать это:

Теперь вы можете написать такой код:

clicks$.subscribe(val => console.log(val));

Окончательный код:

Коснитесь или щелкните «︎ ❤» , чтобы помочь продвинуть этот фрагмент среди других.