combineLatest不订阅参数Observables,不提供数据

时间:2017-05-09 00:51:43

标签: angular rxjs observable combinelatest

我有一个相当常见的用例。我正在从FirebaseDatabase加载一系列对象,并从Firebase存储中附加图像。

FirebaseDatabase根据查询返回Observable。每次db中的数据都会发生变化时,Observable将发出更新的查询结果。我还通过映射到每个条目的[Domain,ImagePromise]元组,将Storage api中的Promise附加到结果中的每个条目

getDomainObj(){
   return this.af.database.list(this.listRef)
    .map((mps: Domain[]) => 
       mps.map((mp: Domain) =>  
         [mp, this.getImage(mp['$key'])]));
}

两个问题:

  1. 有些人没有图像。
  2. 由于图像不在图像中,因此图像更改时不会发出可观察图像。为了解决这个问题,我添加了一个Cloud函数来发送每个图像更新的推送通知(在这个问题的范围之外,它可以工作,所以不需要进入它)。每个mp都是它自己的PubSub主题,我们在客户端订阅该主题,并获得每次上传特定mp的图像时将发出的Observable更新
  3. 我通过映射

    解决了这两个问题
    this.mpSvc.getDomainObj()
      .map((tupleArray: [Domain, Promise<string>][]) => 
        tupleArray.map(([mp, imageSrcPromise]: [Domain, Promise<string>]) => {
          return [mp, 
               Observable.fromPromise(imageSrcPromise) // get initial image
                .startWith(null) // make sure that it emits at least once
                .concat( // updates
                  this.mpSvc.signUpForPictureUpdates(mp['$key'])
                  .map(([id, imageSrc]: [string, string]) => imageSrc))
    
          ];
        })
    )
    

    mpSvc.signUpForPictureUpdates返回一个domainId-imageSrc元组的Observable,每当域的图像重新上载时,它就会发出

    现在最终的目标是拥有一个[Domain,imageSrc]数组的Observable,它将在每次Domain对象更改的任何一个图像或任何Domain对象更改时发出,或者结果为查询更改。

    首先我再次重新映射,而不是发出[Domain,Observable]对的数组,发出一个Observable&lt; [Domain,imageSrc]&gt;

    的数组
    .map((tupleArray: [Domain, Observable<string>][]) => 
      tupleArray.map(([mp, imageSrcObservable]: [Domain, Observable<string>]) => 
         imageSrcObservable.map((imageSrc: string) => [mp, imageSrc])))
    

    现在我有了一对Observable对的Observable。每次域或查询结果发生更改时,父Observable都会发出,每次特定域的图像发生更改时,子项都会发出。

    对于最后一步,我将可观察的子节点组合成一个observable和switchMap。

    .switchMap((imageObservables: Observable<[Domain, string]>[]) => 
      Observable.combineLatest(imageObservables)
    )
    

    结果被正确地包含(事实上,它通过ngFor | async显示在Angular模板中,但这也在范围之外)。

    不幸的是,结果不是预期的结果。实际上,我有四个来自查询的结果对象,两个有图像,两个没有。我所看到的并不一致 - 有时两个图像都会加载,有时一个加载,而且通常都不加载。

    具体来说,如果我要像这样添加一个记录行:

    .do(x => console.log('final', x), x => console.error('finalerror', x), () => console.log('finalcomplete'));
    

    我总是在imageSrc中至少得到一个带空值的日志行,但几乎没有额外的行使用已解析的真实imageSrcs。

    在最后一步之前订阅(combineLatest)正确获取所有数据

    我做错了什么?

    编辑:在进一步研究之后,问题肯定是combineLatest。我尝试用combineLatest替换imageObservables[1].map(x=>[x])并且它完美地工作(虽然当然只返回一个值而不是数组。而且,逐步通过combineLatest导致我进行一些奇怪的结束,因为需要的是在任何时候关闭?

1 个答案:

答案 0 :(得分:2)

好的,我发现了问题。如果Promise拒绝,fromPromise将抛出错误。因此,其中一个孩子在getImage没有获得图片时出错,导致combineLatest完成。

我像这样修理了

Observable.fromPromise(imageSrcPromise) // get initial image
.catch(() => Observable.empty()) // <-- fix 
.startWith(null) // and so forth...

这就是为什么有时它会正常发射,有时它没有。如果首先加载带有图像的Domain对象的请求,则会显示它们。如果首先返回无图像对象,则流将结束。