【问题标题】:Rxjs init resource once with hot observable使用热可观察的 Rxjs 初始化资源一次
【发布时间】:2017-11-13 00:25:56
【问题描述】:

我有以下代码的服务设置:

@Injectable()
export class StaticResourcesService {

private resourcesTree$: Observable<any>;

getSpecificLabel(resourceName: string, type: string): Observable<string> {
    return this.getResources().map(resources => {
      return jp.value(resources, `$.${resourceName}[?(@.type=="${type}")].label`);
    });
  }

private getResources(): Observable<any> {
    if (this.resourcesTree$ != null) {
      return this.resourcesTree$;
    }
    let source = this.getResourcesFilePath() // get obs<string> with a file path
      .mergeMap(path => {
        console.log(`static-resources path: ${path}`);
        return this.http.get(path).map(res => res.json()); // get json obj
      }).do(res => {
        this.resourcesTree$ = Observable.of(res);
      });
    return source;
  }

具有如下变换方法的自定义角管:

transform(resourceType: string, resourceName: string): Observable<string> {
    return this.staticResourcesService.getSpecificLabel(resourceName, resourceType);
  }

我在一个简单的 html 页面中使用了这个自定义管道:

<div *ngFor="let item of items">{{ item?.status | staticResource:'MY_RES' | async }}</div>

当我第一次加载页面时,自定义管道将处理 4 次(ngFor 循环 4x)并且永远不会从 resourcesTree$ 获取其源数据。我知道这是一种正常行为,因为订阅会同时收听,并且会执行 4 次。但我想要的是一种优化的方式,只访问一次数据,然后在接下来的 3 次利用 resourcesTree$。我试图通过在源上使用 share() 方法使其“热”。但我不能让它正常工作。

有人可以告诉我如何实现这一点吗?也许我都错了,我真的很愿意接受这里的任何建议。

谢谢

【问题讨论】:

  • 你确定有可能吗?因为在getSpecificLabel 中,您基本上是在创建 4 个完全不同的可观察对象。 share() 在这里帮不了你。我认为可以通过制作“地图可观察”管道来实现,但这取决于jp.value() 的作用。
  • jp.value 在这里不是很相关,它只帮助我使用 jsonpath 将特定标签检索到我的 json 对象中
  • 你的 observable 有副作用——这可能表明你做错了什么。我认为使用这种方法是不可能的。你能描述一下你到底想要达到什么目标吗?它应该根据item.status字段显示来自URL的JSON文件?
  • 是的,我有一个 json 对象: "MISSION_TYPE": [ { "type": "PRODUCT_DELIVERY", "label": "Livraison produit" }, { "type": "MANUAL_PRODUCT_DELIVERY", "label ": "Livraison produit manuelle" }, { "type": "OTHER__DELIVERY", "label": "Livraison autre" } ] 我想获取每个代码的标签。但我只想访问 json 文件,然后使用我的 resourcesTree$ 变量从内存中获取值。
  • 并且您希望在出现给定状态时下载该 JSON。但是当过去已经下载了 JSON 时,使用缓存的值?

标签: angular typescript rxjs observable system.reactive


【解决方案1】:

异步服务的通常模式是使共享资源成为 Subject() 或 ReplaySubject()。这样,您只需连接订阅者,他们就会很好地等待数据到达。

在您的示例中,它正在对资源进行切片,因此这是一个地图运算符(正如您已经拥有的那样)。还有一些表示提取已经开始的方式(不管它是否已经完成,因为它是可观察的)。

由于不再从外部订阅 getResources(),因此您需要将 .do() 替换为 .subscribe()。

@Injectable()
export class StaticResourcesService {

private resourcesTree$ = new ReplaySubject(1);
private status = 'unloaded';

getSpecificLabel(resourceName: string, type: string): Observable<string> {
  if( this.status === 'unloaded') {
    this.status = 'loading';
    getResources();   
  }
  return resourcesTree$.map(resources => {
    return jp.value(resources, `$.${resourceName}[?(@.type=="${type}")].label`);
  });
}

private getResources(): Observable<any> {
  this.getResourcesFilePath() // get obs<string> with a file path
    .mergeMap(path => {
      console.log(`static-resources path: ${path}`);
      return this.http.get(path).map(res => res.json()); // get json obj
    })
    .subscribe(res => {
        this.resourcesTree$.next(res);
        this.status = 'loaded';
      });
  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-15
    • 1970-01-01
    • 2017-10-15
    • 1970-01-01
    • 2017-04-13
    • 1970-01-01
    • 1970-01-01
    • 2017-09-05
    相关资源
    最近更新 更多