从不断增长的数组中重复发出值

Repeatedly emitting values from a growing array

快速上下文:我正在显示一个“实时”图像幻灯片,这意味着在显示图像时,用户可以上传图像并且应该显示那些新图像(最终)。这是“无限”的,这意味着我想在所有图像都发出后从第一张图像重新开始。新传入的图像应该进入队列的末尾并在我循环返回它们时发出。

我想出了一个我认为是肮脏且不正确的解决方案,归结为:

const srcArray = [-2, -1];
const userImageUpload$ = interval(2000).pipe(
  tap((val) => srcArray.push(val))
).subscribe();

const liveImages$ = zip(interval(1000), from(srcArray)).pipe(
  map(([a, b]) => b),
  repeat()
).subscribe((val) => console.log("Image " + val));

这里我们有一些“图像”的起始数组。 userImageUpload$ 表示新图像传入的想法。当我得到一个时,我将它推入起始数组。 liveImages$ 表示我想要发出的值。这在技术上是可行的,但改变这个共享的全局数组感觉很糟糕并且可能会导致问题。是否有一个很好的解决方案可以通过使用主题来完成这一切?我无法想出解决方案。

使用BehaviorSubject更有意义。我认为你的解决方案很好,这个答案的不同之处在于当图像上传时你会收到通知,即数组得到更新,通过使用 imageStore.

开始流
const imageStore=new BehaviorSubject([-2, -1]);

const userImageUpload$ = interval(2000).pipe(
  map((val) => imageStore.getValue().concat(val))
).subscribe(imageStore);

imageStore.pipe(switchMap(srcArray=>
 zip(interval(1000), from(srcArray)).pipe(
   map(([a, b]) => b),
   repeat()
  )
))

您可以这样尝试,您可以在我的 stackblitz 中看到相同的内容(Angular 示例) https://stackblitz.com/edit/angular-ivy-dhvt8r?file=src/app/app.component.html

private images: BehaviorSubject<number[]> = new BehaviorSubject<number[]>([
    -2,
    -1
  ]);
  private imagesChanged$ = this.images.asObservable();

  imageToDisplay$: Observable<number>;

  ngOnInit() {
    this.imageToDisplay$ = this.imagesChanged$.pipe(
      switchMap(() => interval(1000)),
      map((v: number) => v % this.images.value.length),
      map((v: number) => this.images.value[v])
    );
  }

  addImage() {
    this.images.next([
      ...this.images.value,
      this.images.value[this.images.value.length - 1] + 1
    ]);
  }
<div>
    Current Image: {{ imageToDisplay$ | async }}
</div>

<button (click)="addImage()">Add a Image</button>

我会保留一个 currentImages$ observable 来累积所有上传的图像,以及一个 currentImageIndex$ observable 来保存当前应该显示的图像的索引。 然后 liveImages Observable(结合了两者)就是你想要的结果。

const currentImages$ = userImageUpload$.pipe(
    scan((acc, curr) => [...acc, curr], [])
);

const currentImageIndex$ = interval(SLIDE_SHOW_INTERVAL).pipe(
    withLatestFrom(currentImages$),
    map(([, currImages]) => currImages),
    scan((accIndex, currImagesArr) => (accIndex + 1) % currImagesArr.length, 0)
);

const liveImages$ = currentImageIndex$.pipe(
    withLatestFrom(currentImages$),
    map(([currIndex, currImages]) => currImages[currIndex])
);