Реактивное программирование

Реактивное программирование - это парадигма декларативного программирования, связанная с потоками данных и распространением изменений. Рассмотрим ниже пример.

b = 10, c = 20;
a$ = b+c;
b = 20, c = 30;
a$ = ?

В мире императивного программирования, когда эта программа выполняется, значение a $ всегда будет 30. Даже если значения b и c обновлены, это не повлияет на значение a $. В случае реактивного программирования значение a $ будет 50. Здесь a $ - это постоянная переменная, которая автоматически обновляется всякий раз, когда значения b или c меняются, без необходимости повторного выполнения оператора программы. Как вы заметили, реактивное программирование влечет за собой психологический сдвиг в том, как вы рассуждаете о поведении своей программы.

RxJS

RxJS - это библиотека JavaScript для реактивного программирования с использованием Observables, чтобы упростить составление асинхронного кода или кода на основе обратного вызова. Первоначально начатый как проект с открытым исходным кодом, RxJS в настоящее время развивается как управляемый сообществом проект, принадлежащий Бену Лешу из Netflix, санкционированный Microsoft как RxJS 5.

Ниже приведены основные концепции, которые следует понимать при изучении RxJS
Наблюдаемый, Наблюдатель, Подписка, Оператор, Тема, Протокол Push vs Pull.
Чтобы понять эти концепции простым способом , в качестве метафорического примера рассмотрим нижеприведенный случай службы доставки продуктов.

Допустим, вы (потребитель) заинтересованы зарегистрироваться в некоторых службах доставки продуктов (производитель), таких как Amazon Fresh, HelloFresh или Instacart, поскольку вы не хотите идти в продуктовый магазин вручную, чтобы забрать товары. Первый шаг - зарегистрироваться в какой-либо подписке на эти услуги, чтобы автоматически получать поток продуктовых ящиков с течением времени (еженедельно или раз в две недели). Получив коробку с продуктами, вы обычно выполняете какое-либо действие, например готовите или храните ее в морозильной камере для использования на следующий день. Вы можете считать этот процесс реактивным, потому что вы реагируете на получение коробки с продуктами.

Теперь давайте свяжем тот же сценарий с концепциями RxJS.

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

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

А теперь представьте, Оператор - это своего рода способ настроить вашу подписку на Observable. Используя оператора, вы можете контролировать, как часто вы хотите получать свои пакеты, удерживать подписку, добавлять или удалять элементы и т. Д.

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

Теперь давайте разберемся, что такое протоколы Pull и Push на том же примере. В системах вытягивания Потребитель определяет, когда он получает данные от Производителя. Сам производитель не знает, когда данные будут доставлены потребителю. Это похоже на то, что вы собираетесь хранить, чтобы забрать предметы.
Каждая функция или итератор в JavaScript - это система вытягивания. В то время как в системах push производитель (продуктовые услуги) определяет, когда отправлять данные Потребителю (вам). Потребитель не знает, когда он получит эти данные. Promises и Observables - наиболее распространенный тип систем Push. Но в отличие от Observables, Promises не могут обрабатывать источники данных, которые производят несколько значений, например движения мыши или последовательности байтов в файловом потоке.

Надеюсь, теперь у вас есть понимание на высоком уровне ключевых концепций Rxjs. Давайте углубимся в технические определения и код.

Observable - это объект-оболочка для источника данных с потоком.

import { Observable } from ‘rxjs’;
const observable = Observable.create(function (observer) {
 observer.next(1);
 observer.next(2);
 setTimeout(() => {
 observer.next(3);
 observer.complete();
 }, 1000);
});

Вышеупомянутая программа создает настраиваемый Observable, импортированный из библиотеки rxjs, который сразу (синхронно) помещает значения 1 и 2 при подписке, а значение 3 по прошествии одной секунды с момента вызова подписки, затем завершается. Когда вы запускаете эту программу, вывод не будет отображаться, поскольку никто не слушает этого наблюдателя. Как объяснялось выше, чтобы вызвать Observable и увидеть значения, нам нужна подписка на наблюдателя.

Технически Observer - это просто объект с методами next (v), error (e) и complete (). Observable вызовет метод next (value) наблюдателя для предоставления данных. Точно так же вызывает ошибку (err) наблюдателя, чтобы выдать любую ошибку, или метод complete () наблюдателя, когда поток завершен.

const subscription =observable.subscribe({
 next: x => console.log(‘got value ‘ + x),
 error: err => console.error(‘something wrong occurred: ‘ + err),
 complete: () => console.log(‘done’),
});
console.log(‘just after subscribe’);
// got value 1
// got value 2
// just after subscribe
// got value 3

Когда вы подписываетесь на Observable, вы получаете обратно подписку, которая представляет текущее выполнение. Нам нужно явно вызвать unsubscribe (), чтобы отменить выполнение.

subscription.unsubscribe();

До сих пор мы видели настраиваемый Observable с использованием Rx.Observable.create. Но класс Rx.Observable имеет множество функций для создания наблюдаемых из разных типов данных / потоков, таких как события, шаблоны событий, массивы, обещания, одиночные / множественные значения, любые структуры данных / примитивные типы данных и так далее. Ниже приведены несколько примеров часто используемых Observable.from и fromEvent.

import { Observable } from ‘rxjs’;
Observable.from([10, 20, 30]).subscribe(x => console.log(x));
const link = document.querySelector('#google');                      
Observable.fromEvent(link, 'click')           
  .map(event => event.currentTarget.getAttribute('href'))           
  .subscribe(console.log); //-> http://www.google.com

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

Rx.Observable.from(<data-source>) 1
 .operator1(…)                    2
 .operator2(…)
 .operator3(…)
 .subscribe(<process-output>);    3
• 1 Wraps a data source with a stream
• 2 Invokes a sequence of operations chained by the dot operator. 
• 3 Processes the results

Перейдите по этой ссылке, чтобы увидеть наиболее часто используемые операторы.

Наконец, RxJS Subject - это особый тип Observable, который позволяет передавать значения множеству Observer'ов, тогда как простой «unicast Observable» отправляет уведомления только одному Observer'у. Рассмотрим ниже пример с несколькими наблюдателями, подписанными на одну и ту же тему.

import { Subject } from ‘rxjs’;
 const subject = new Subject<number>();
 subject.subscribe({
 next: (v) => console.log(`observerA: ${v}`)
});
subject.subscribe({
 next: (v) => console.log(`observerB: ${v}`)
});
 
const observable = Observable.from([1, 2]);
observable.subscribe(subject);
 
// Logs:
// observerA: 1
// observerB: 1
// observerA: 2
// observerB: 2

Это поведение отличается, когда несколько наблюдателей подписаны на один и тот же Observable, как показано ниже.

const observable = Observable.from([1, 2]);
 observable.subscribe({
 next: (v) => console.log(`observerA: ${v}`)
});
observable.subscribe({
 next: (v) => console.log(`observerB: ${v}`)
});
 // Logs:
// observerA: 1
// observerA: 2
// observerB: 1
// observerB: 2

Вот и все!. Надеюсь, эта статья помогла вам понять некоторые основные концепции реактивного программирования и RxJS.

Ваше здоровье

Навин Чандупатла