Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

用RxJS把分次查询聚合为批量查询 #18

Open
myml opened this issue Aug 31, 2018 · 0 comments
Open

用RxJS把分次查询聚合为批量查询 #18

myml opened this issue Aug 31, 2018 · 0 comments
Labels

Comments

@myml
Copy link
Owner

myml commented Aug 31, 2018

现有后端接口 /user?uid=1可根据uid查询用户信息,uid可为数组,例如/user?uid=1和/user?uid=1&uid=2&uid=3都是合法查询
在前端封装getUserInfo函数

getUserInfo(...uidList: number[]) {
    uidList=Array.from(new Set(uid))
    const params = uidList.reduce(
      (acc, uid) => params.append('uid', uid.toString()),
      new HttpParams(),
    );
    return this.http.get('/user', { params });
}

为了在angular更方便的使用,创建userinfo管道

transform(uid: number) {
  return getUserInfo(uid); 
}

前端页面使用uid|userinfo|async as user就能在模板使用用户信息
这样做缺陷是会发送多次请求,于是通过rxjs丰富的管道进行缓存,来实现把管道的参数累加到一次查询,然后结果再根据uid筛选

初优化:使用bufferTime

bufferTime会按时间把值搜集到数据,然后发送

pipe(
      bufferTime(10),
      filter(uidList => list.length > 0),
      switchMap(uidList => getUserInfo(...uidList)),
      share(),
)

当时间段内没有值bufferTime也会发送空数组,所以中间加了筛选。share用来在多个管道之间共享订阅
这样做缺陷是时间间隔不好定,定大了,界面会有一定空白期来等待buffer和接口查询。定小了,浪费cpu

二次优化:使用debounceTime

使用scan和debounceTime

pipe(
      scan((acc, uid) => acc.concat(uid), [] as number[]),
      debounceTime(10),
      switchMap(uidList => getUserInfo(...uidList)),
      share(),
)

这样就省去了不停止运行的定时器,也能最低程度的减少界面延迟显示。

解决BUG:share

上面实现的管道有个bug

  • 总漏掉第一个管道传递的值
  • 翻页的问题
    两个都与share有关。
    管道函数实现如下
  transform(uid: number) {
    this.buffer.next(uid)
    return this.result.pipe(
      map(infoList => {
        return infoList.find(info => info.uid === uid);
      }),
      first(),
    );
  }

把uid传递给buffer,然后等待result结果,再进行筛选。直接next是漏掉第一个uid的原因,此时函数还未返回,result没有订阅者,而share在没有订阅者时,也会断开对底层源Observable的订阅,就这样漏掉了这个数据,第二个管道调用时因为有了第一个管道的订阅,所以管道工作正常。加上setTimeout 0,异步的调用next解决。
第二个bug:scan是连续累计所有值的,单个组件共享同一个管道,当在组件内进行翻页时,查询应该也会包括上一页的uid,这样就会造成多次翻页后,累加过多的uid,造成查询量过大。
实际却并没有发生这种情况,因为share的特性,在翻页时,所有前端页面的管道被清除,share取消订阅,当新页面刷新出现时,管道又被添加,share重新订阅,scan也是一个新的,毕竟share之前是一个冷的 Observables,订阅者有单独的发送者,这个bug就被share的特性巧妙的解决了。

@myml myml added the rxjs label Aug 31, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant