在map函数中使用Observable的先前值

时间:2017-04-21 09:06:20

标签: rxjs observable

我有一个由事件产生的图像列表流,如下所示:

const imageSrcs$ = Rx.Observable.fromEvent($('#clicker'), 'click')
  .flatMap(() => this.imageService.getImagesForUser(userUid))
  .scan((acc, cur) => acc.concat(cur), [])
  .startWith([]);

imageService.getImagesForUser(userId)从数据库中检索有限数量的图像并返回图像列表:

[
   {
       id: number,
       timestamp: string,
       ...
    },...
]

所以这个observable只接受该结果并将累加器(scan())应用于它。但是,getImagesForUser还需要一个额外的参数startAt,这是开始检索的时间戳。对于我的情况,这应该是observable返回的数组中最后一个图像的时间戳。

当然,我可以这样做:

lastTimestamp;
...
const imageSrcs$ = Rx.Observable.fromEvent($('#clicker'), 'click')
  .flatMap(() => this.imageService.getImagesForUser(userUid, this.lastTimestamp))
  .scan((acc, cur) => acc.concat(cur), [])
  .startWith([])
  .subscribe(list => this.lastTimestamp = list[list.length - 1].timestamp);

但这似乎并不像关注点的分离,我有兴趣知道我是否可以在一个可观察的情况下做到这一点。我认为这样的事情会起作用:

const imageSrcs$ = Rx.Observable.fromEvent($('#clicker'), 'click')
  .startWith([])
  .filter(val => Array.isArray(val))
  .last()
  .map(val => val[val.length - 1].timestamp)
  .flatMap(startAt => this.imageService.getImagesForUser(userUid, startAt))
  .scan((acc, cur) => acc.concat(cur), []);

但当然,它并不是因为点击事件被过滤掉了。

1 个答案:

答案 0 :(得分:2)

我能想到的最简单的方法就是这样(这只是模拟你的用例,但我认为你会明白这一点):

function getImagesForUser(startAt) {
  return Observable
    .range(startAt * 5, 5)
    .toArray();
}

const imageSrcs$ = Observable.defer(() => {
    let startAt = 0;
    return Observable.timer(0, 1000)
      .flatMap(() => getImagesForUser(startAt))
      .do(arr => startAt = arr[arr.length - 1]);
  })
  .scan((acc, cur) => acc.concat(cur), [])
  .startWith([])
  .subscribe(console.log);

最终,scan()运算符也可以进入.defer()回调。

而不是点击我正在使用Observable.timer(0, 1000)。我不知道在没有使用Observable链之外的任何状态变量的情况下是否有更好的方法。