Как разработчики интерфейса, мы сталкиваемся с 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() с сообщением, функция подписки вызывается с сообщением в качестве аргумента.

Несколько «продвинутых» комментариев к этому примеру:

  1. Это очень упрощено, потому что, например, в библиотеке RxJS у вас может быть несколько подписок на один наблюдаемый объект, и есть другие методы, такие как error() и complete().
  2. Этот пример на самом деле является примером Темы. Это по-прежнему означает, что это 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!

Затем, когда вызывается метод подписки, он делает две вещи:

  1. точно так же, как и раньше, он сохраняет или фактически ссылается на эту функцию в свойстве.
  2. но теперь он также вызывает _observerFunction с собой (this) в качестве аргумента.

Затем выполняется функцияObserverFunction, вызывается метод next() (на самом Observable), а остальное аналогично предыдущему примеру.

Конечно, Observables в таких библиотеках, как RxJS, более сложны. Но я думаю, что вы должны начать с простого, чтобы получить основные понятия.

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

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

Заключение

Наблюдаемые трудно полностью понять и понять. Они построены (по крайней мере, в JS) на концепциях функционального программирования; что вы можете передать функцию в качестве аргумента, «сохранить» ее (фактически сослаться на нее), а затем вызвать ее из другого контекста или в другое время.

На мой взгляд, поскольку наблюдаемые объекты часто используются в экосистеме JS, важно понимать, как они работают внутри.