RxJS — .subscribe() против .publish().connect()

#angular #rxjs #rxjs5

#angular #rxjs #rxjs5

Вопрос:

В основном это вопрос о наилучшей практике / подходе RxJS, поскольку мой POC-код работает, но я совершенно новичок в RxJS.

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

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

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

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

выход из системы.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.альтернатива.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() заключается в том, что они подписываются на свой наблюдаемый источник. Рассмотрим следующее наблюдаемое:

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

Этот наблюдаемый объект передает все значения наблюдателю сразу после его подписки. Итак, если у меня есть два наблюдателя, то они получают все значения по порядку:

 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 . Этот наблюдаемый объект не подписывается на свой исходный код ( 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

Комментарии:

1. Итак, в моем случае, когда я просто хочу, чтобы observable выполнялся в основном немедленно, вызов .subscribe() без аргументов, вероятно, является наиболее кратким способом выполнения этого и .publish().connect() выполняет это обходным путем.

2. @ClaytonK В целом да, большую часть времени subscribe() достаточно.

3. Почему меняется порядок вывода? оба примера показывают, что obs2 был вторым, кто подписался? Похоже ли это .share() ?

4. @Royi Это из-за того, как Observable.from работает внутренне. Когда вы подписываетесь, он немедленно начинает выдавать все свои значения. Это означает, что без использования какого-либо планировщика он передаст все свои значения (элементы массива) наблюдателю, который подписался прямо сейчас. Затем снова ко второму наблюдателю. С помощью ConnectableObservable и connect() мы сначала подписываемся на обоих наблюдателей, а затем на источник Observable.from . Это означает, что он будет выполнять итерацию массива только один раз и передавать каждое значение обоим наблюдателям одновременно.

5. таким образом, publish эффективно возвращает Subject , правильно? и чем это отличается от multicast operator?

Ответ №2:

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

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

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

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

auth.servive.ts

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

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

выход из системы.component.ts

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