Как разработчики интерфейса, мы сталкиваемся с Observables как с классами, объектами или шаблонами, будь то в Angular, RxJS или через Redux (Redux использует шаблон Observable).
Я попытаюсь раскрыть «магию», стоящую за Observables в javascript, создав два их очень простых примера. Эти примеры являются очень упрощенными версиями.
Давайте рассмотрим первый пример, класс BasicObservable:
class BasicObservable { _subscription; next(val) { if (this._subscription) this._subscription(val) } subscribe(func) { this._subscription = func; } } // Usage: const obs= new BasicObservable(); // creating the object document.querySelector("#my-button").onclick = () => { obs.next('clicked'); // calling next to pass a message to the subscribers } obs.subscribe (message => { console.log(message); // this will fire every time you click the button }
Что здесь происходит?
Когда мы создаем новую наблюдаемую, мы создаем объект как минимум с тремя свойствами (фактически двумя методами и свойством):
subscribe(subscription) — метод, который получает в качестве аргумента функцию (назовем его подпиской). Затем метод подписки ссылается на эту функцию в «частном» свойстве _subscription.
_subscription: хранит функцию подписки и вызывается внутри next().
next(message) — которая будет передавать сообщения, вызывая функцию подписки, хранящуюся в _subscription.
Таким образом, каждый раз, когда мы вызываем next() с сообщением, функция подписки вызывается с сообщением в качестве аргумента.
Несколько «продвинутых» комментариев к этому примеру:
- Это очень упрощено, потому что, например, в библиотеке RxJS у вас может быть несколько подписок на один наблюдаемый объект, и есть другие методы, такие как error() и complete().
- Этот пример на самом деле является примером Темы. Это по-прежнему означает, что это Observable с возможностью отправлять из него сообщения (вызов next()). Много дискуссий о разнице между субъектом и наблюдаемым, вроде этого.
Давайте сделаем еще один шаг к тому, как в основном используются наблюдатели, по крайней мере, в angular:
class SimpleObservable { constructor(observerFunction) { this._observerFunction = observerFunction; } _subscription; _observerFunction; next(val) { if( _subscription) this._subscription(val) } subscribe(func) { this._subscription = func; this._observerFunction(this) } } // Usage: const obs = new SimpleObservable(observer => { observer.next('started'); setTimeout(() => { observer.next('finished after 2 seconds') }, 2000) }) obs.subscribe((res) => { console.log(res); // 'started' and later 'finished after 2 seconds' })
Что изменилось по сравнению с предыдущим примером?
Мы добавили еще одно свойство: _observerFunction, и когда мы создаем Observable, нам нужно передать одно значение:ObserverFunction. Затем на эту функцию ссылается функция _observerFunction.
Особенность функции наблюдателя в том, что это функция, которая получает один аргумент: Observable!
Затем, когда вызывается метод подписки, он делает две вещи:
- точно так же, как и раньше, он сохраняет или фактически ссылается на эту функцию в свойстве.
- но теперь он также вызывает _observerFunction с собой (this) в качестве аргумента.
Затем выполняется функцияObserverFunction, вызывается метод next() (на самом Observable), а остальное аналогично предыдущему примеру.
Конечно, Observables в таких библиотеках, как RxJS, более сложны. Но я думаю, что вы должны начать с простого, чтобы получить основные понятия.
Все это функциональное программирование вызывает настоящую головную боль: метод, который получает функцию в качестве аргумента, сохраняет ее, и другой метод, который вызывает эту функцию позже. Класс, который получает функцию в качестве аргумента для своего конструктора, а затем вызывает эту функцию с самим собой в качестве аргумента. Все еще со мной? Я знаю, что потерялся.
Но есть и преимущества, в основном очень мощный способ связи между разными частями вашего кода, при этом большую часть времени его внутренности абстрагируются от вас. Тем не менее, я считаю, что это помогает понять, что происходит под капотом. Вот почему я попытался объяснить основные механизмы, как я их вижу. Надеюсь, поможет.
Заключение
Наблюдаемые трудно полностью понять и понять. Они построены (по крайней мере, в JS) на концепциях функционального программирования; что вы можете передать функцию в качестве аргумента, «сохранить» ее (фактически сослаться на нее), а затем вызвать ее из другого контекста или в другое время.
На мой взгляд, поскольку наблюдаемые объекты часто используются в экосистеме JS, важно понимать, как они работают внутри.