Источник с `publishReplay ()` и `refCount ()` не работает должным образом между подписками в нескольких компонентах в Angular - PullRequest
0 голосов
/ 26 мая 2020

Я пытаюсь понять, как работает publishReplay в rx js. У меня есть пример, в котором он работает должным образом:

const source = new Subject()
const sourceWrapper = source.pipe(
  publishReplay(1),
  refCount()
)

const subscribeTest1 = sourceWrapper
.subscribe(
  (data: any) => {
    console.log('subscriber1 received a value:', data)
  }
)

source.next(1)
source.next(2)

setTimeout(() => {
  const subscribeTest2 = sourceWrapper
  .subscribe(
    (data: any) => {
      console.log('subscriber2 received a value:', data)
    }
  )
}, 5000)

У нас есть тема и обертка, в которую добавлены publushReplay(1), refCount(). Мы создаем первую подписку, а затем выдаем 2 значения. В результате мы видим в консоли следующее:

subscriber1 received a value: 1
subscriber1 received a value: 2

И через 5 секунд создаем еще одну подписку, которая получает последнее буферизованное значение из ReplaySubject, созданного publishReplay(1). В результате через 5 секунд мы увидим еще одну заметку в консоли:

subscriber2 received a value: 2

Ссылка на stackblitz

Пока все хорошо.

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

Он содержит один модуль с 2 компонентами app.component.ts и hello.component.ts и 1 сервис test.service.ts

test.service .ts

@Injectable({
  providedIn: 'root'
})

export class TestService {
  private _test$: Subject<string> = new Subject()

  public get test$(): Observable<string> {
    return this._test$.pipe(
      publishReplay(1),
      refCount()
    )
  }

  public pushToStream(val: string): void {
    this._test$.next(val)
  }
}

app.component.ts

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
  public name = 'Angular';

  constructor(private testService: TestService) {}

  public ngOnInit(): void {
    this.testService.test$.subscribe((data: string) => {
      console.log('Subscriber in app.component received a value: ', data)
    })
    this.testService.pushToStream('new value')
    this.testService.pushToStream('one more value')
  }
}

app.component. html

<hello name="{{ name }}"></hello>
<p>
  Start editing to see some magic happen :)
</p>

hello.component.ts

@Component({
  selector: 'hello',
  template: `<h1>Hello {{name}}!</h1>`,
  styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent implements OnInit  {
  @Input() name: string;

  constructor(private testService: TestService) { }

  public ngOnInit(): void {
    setTimeout(() => {
      this.testService.test$.subscribe((data: string) => {
        console.log('Subscriber in hello component received a value:', data)
      })
    }, 4000)
  }

}

Здесь тот же принцип: удерживайте источник в службе, который является синглтоном, создайте 1 подписку в app.component.ts, выдать 2 значения и создать еще одну подписку с задержкой в ​​hello.component.ts. Как мы могли видеть в предыдущем примере, вторая подписка должна получить последнее буферизованное значение, но это не так. В консоли мы видим только следующее:

Subscriber in app.component received a value: new value
Subscriber in app.component received a value: one more value

Что мне не хватает и почему это не работает?

1 Ответ

2 голосов
/ 26 мая 2020

Похоже, что ссылка на оболочку для объекта теряется при возврате с использованием метода доступа (получателя) или функции-члена (например, getTest() { return this.test$ }). Если значение test$ объявлено public и к нему осуществляется прямой доступ, ссылка на наблюдаемый источник, по-видимому, сохраняется среди различных компонентов. Попробуйте следующее

Service

export class TestService {
  private _test$: Subject<string> = new Subject()

  public test$ = this._test$.pipe(
    publishReplay(1),
    refCount()
  );

  public pushToStream(val: string): void {
    this._test$.next(val)
  }
}

Я изменил ваш Stackblitz

Вероятно, функции возвращают отдельные копии переменных, которые связаны с один и тот же объект наблюдаемый, но они (индивидуальный test$ в компонентах) не один и тот же экземпляр, а несколько отдельных экземпляров.

Возможно, кто-то другой сможет дать канонический ответ.

...