RxJS -.subscribe() vs .publish(). Connect()

Это, в основном, вопрос с лучшей практикой/подходом RxJs, так как мой код POC работает, но я совершенно новый для RxJs.

Вопрос сводится к .subscribe() vs .publish().connect(), так как оба они делают то же самое.

В моем приложении angular2 у меня есть кнопка, которая вызывает функцию для входа пользователя в систему, которая вызывает функцию в моей службе, которая выполняет некоторые действия на стороне сервера и возвращает мне URL-адрес для перенаправления пользователя. Чтобы инициировать запрос, я вызываю .subscribe(), чтобы заставить наблюдаемое начать создавать значения. Я читал статью "Холодные против горячих наблюдателей", и другой подход заключался бы в вызове .publish().connect() вместо .subscribe(). Есть ли какая-либо польза для любого подхода.

<a (click)="logout()">Logout</a>

Функция выхода выглядит следующим образом:

logout.component.ts

logout() { this.authService.logout(); }

И сервис (фактический выход) выглядит следующим образом:

auth.service.ts

logout() : Observable<boolean>  {
        this.http.get(this.location.prepareExternalUrl('api/v1/authentication/logout'))
            .map(this.extractData)
            .catch(this.handleError)
            .do((x: string) => { window.location.href = x; })
            .subscribe();    // Option A - 

        return Observable.of(true);

    }

auth.service.alternative.ts

logout() : Observable<boolean>  {
        this.http.get(this.location.prepareExternalUrl('api/v1/authentication/logout'))
            .map(this.extractData)
            .catch(this.handleError)
            .do((x: string) => { window.location.href = x; })
            .publish()  // Option B - Make connectable observable
            .connect(); // Option B - Cause the connectable observable to subscribe and produce my value       

        return Observable.of(true);
    }

Ответ 1

Разница между subscribe() и .publish().connect() заключается в том, что они подписываются на свой источник Observable. Рассмотрим следующее наблюдение:

let source = Observable.from([1, 2, 3])

Этот Observable присваивает все значения правилу Observer, когда он подписывается. Поэтому, если у меня есть два наблюдателя, они получают все значения в порядке:

source.subscribe(val => console.log('obs1', val));
source.subscribe(val => console.log('obs2', val));

Это будет печатать на консоли:

obs1 1
obs1 2
obs1 3
obs2 1
obs2 2
obs2 3

С другой стороны, вызов .publish() возвращает ConnectableObservable. Этот Observable не подписывается на источник (source в нашем примере) в его конструкторе и сохраняет свою ссылку. Тогда вы можете подписаться на несколько наблюдателей, и ничего не происходит. Наконец, вы вызываете connect(), а ConnectableObservable подписывается на source, который начинает испускать значения. На этот раз уже есть два Наблюдателя, которые подписываются, поэтому они выставляют значения для обоих из них по одному:

let connectable = source.publish();
connectable.subscribe(val => console.log('obs1', val));
connectable.subscribe(val => console.log('obs2', val));
connectable.connect();

Что печатает на консоли:

obs1 1
obs2 1
obs1 2
obs2 2
obs1 3
obs2 3

Смотрите демо-версию: http://plnkr.co/edit/ySWocRr99m1WXwsOGfjS?p=preview

Ответ 2

Это немного уклоняется от вашего вопроса, но вы можете счесть его полезным:

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

  • отменить поток
  • изменить поток
  • определить, была ли операция успешной.

Вместо этого я бы сделал:

auth.servive.ts

logout() : Observable<string>  {
       return this.http.get(...).map(this.extractData)          
            .catch(this.handleError);
}

Теперь вызывающий код может делать все, что захочет, с результирующим URL-адресом

logout.component.ts

logout(){
    this.authService.logout().subscribe(
        url => window.location.href = url,
        err => {
            /*todo: handle if error was thrown by authService.handleError*/
        }
    );
}