使用Rxjs缓存http获取响应的性能原因

时间:2016-09-11 15:14:45

标签: angular rxjs

潜在的“重复”问题没有回答我的问题 - 我可能认为我对Observables的使用与示例不同,正如我所指出的,我尝试使用share()失败。

我有一个看起来像这样的服务:

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Card } from '../../card';
import { Observable } from 'rxjs/Rx';

@Injectable()
export class CardService {
    private privateUrl = 'http://localhost:3335/cardname/';
    constructor (private http: Http) {}

    public getDetailedInfo(card): Observable<Card> {
        return this.http.get(`${this.privateUrl}${card.name}`)
            .map(this.extractData);
    }

    private extractData(res: Response) {
        let body = res.json();
        return new Card(body.data.name, body.data.card_type, body.data.text);
    }
}

使用它的组件看起来像:

import { Component, OnInit } from '@angular/core';
import { Card } from '../../card';
import { CardService } from '../../services/card/card.service';
import { CARDNAMES } from '../../cards';

@Component({
    selector: 'main',
    styles: [require('./main.component.scss').toString()],
    template: require('./main.component.html'),
    providers: [CardService]
})

export class MainComponent implements OnInit {
    mode = 'Observable';
    title = 'YuGiOh Deck Browser';
    cardnames = CARDNAMES;
    card: Card;
    selectedCard: Card;
    dataStore: Array<any> = [];

    constructor(private cardService: CardService) { }

    ngOnInit() {
        for (let prop in this.cardnames) {
            this.getDetailedInfo(this.cardnames[prop]);
        }
    }

    onSelect(card: Card): void {
        this.getDetailedInfo(card);
    }

    getDetailedInfo(card) {
        this.cardService.getDetailedInfo(card)
            .subscribe((card: Card) => {
                this.card = this.selectedCard = card;
                this.dataStore.push(card);
            });
    }
}

我的模板是:

<div *ngIf="card">
    <h2>{{card.name}}</h2>
    <div class="card-type">{{card?.card_type}}</div>
    <div class="card-description">{{card?.text}}</div>
</div>

我的卡片看起来像这样:

export class Card {
    response:string;
    constructor(
        public name: string,
        public card_type: string,
        public text: string
    ) { }
}

我在阅读thisthat之后尝试使用share()cache()publishReplay(1).refCount(),但似乎没有什么可以阻止点击时获取HTTP请求处理程序被调用。

我误解了这些方法实际上做了什么吗?

我还尝试在this first answer in this question之后将数据保存在内存中的对象数组(代码示例跟随)中,这些对象部分有效,但在单击并触发onSelect事件时失败。

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Card } from '../../card';
import { Observable } from 'rxjs/Rx';

@Injectable()
export class CardService {
    private dataStore: Array<any> = [];
    private observable: Observable<Card>;
    private privateUrl = 'http://localhost:3335/cardname/';
    constructor (private http: Http) {}

    public getDetailedInfo(card): Observable<any> {
        if (this.dataStore.length > 0) {
            this.dataStore.forEach((value, index) => {
                return new Card(
                    this.dataStore[index].data.name,
                    this.dataStore[index].data.card_type,
                    this.dataStore[index].data.text
                )
            });
        } else if (this.observable) {
            return this.observable;
        } else {
            return this.http.get(`${this.privateUrl}${card.name}`)
                .map(
                    (res) => {
                        let body = res.json();
                        this.dataStore.push(body);
                        return new Card(body.data.name, body.data.card_type, body.data.text);
                    }
                );
        }
    }
}

    VM99537:93 EXCEPTION: Error in ./MainComponent class MainComponent - inline template:4:8

2016-09-11 21:04:03.241 zone.js?c625:269 Uncaught EXCEPTION: Error in ./MainComponent class MainComponent - inline template:4:8

ORIGINAL EXCEPTION: TypeError: Cannot read property 'subscribe' of undefined

ORIGINAL STACKTRACE:
TypeError: Cannot read property 'subscribe' of undefined
    at MainComponent.getDetailedInfo (eval at <anonymous> (http://localhost:5000/bundle.js:965:2), <anonymous>:32:13)
    at MainComponent.onSelect (eval at <anonymous> (http://localhost:5000/bundle.js:965:2), <anonymous>:27:14)
    at DebugAppView._View_MainComponent1._handle_click_0_0 (MainComponent.ngfactory.js:172:35)
    at eval (eval at <anonymous> (http://localhost:5000/common.js:1731:2), <anonymous>:381:24)
    at eval (eval at <anonymous> (http://localhost:5000/common.js:2322:2), <anonymous>:255:36)
    at eval (eval at <anonymous> (http://localhost:5000/common.js:2364:2), <anonymous>:27:111)
    at ZoneDelegate.invoke (eval at <anonymous> (http://localhost:5000/vendor.js:472:2), <anonymous>:332:29)
    at Object.onInvoke (eval at <anonymous> (http://localhost:5000/common.js:1551:2), <anonymous>:53:41)
    at ZoneDelegate.invoke (eval at <anonymous> (http://localhost:5000/vendor.js:472:2), <anonymous>:331:35)
    at Zone.runGuarded (eval at <anonymous> (http://localhost:5000/vendor.js:472:2), <anonymous>:239:48)

1 个答案:

答案 0 :(得分:0)

您的数据存储是个好主意,但我发现您的实施存在一些问题。最大的一点是,当组件调用Service.getDetaileInfo时,它需要一个Observable<Card>,但这在代码的两个实例中失败:

//this returns a Card instead of an Observable<Card>
if (this.dataStore.length > 0)... return New Card

//this never seems to be true since you never assign anything to this.observable
else if (this.observable) return this.observable

我的服务看起来像:

//object that holds cards already fetched from server
private cache = {};
private url = '...';

/** takes Card.name and returns Observable that emits detailed info
    from the cache if possible, from the server if not */
public getDetailedInfo(name):Observable<Card> {

   // if a Card by that name is in the cache, return it as an Observable
   if (this.cache[name]) return Observable.of(this.cache[name]);

   //call the server and convert results to JS object and access .data
   return this.http.get('...').map( res => res.json().data)

               // transform object to a new Card
              .map(data => new Card(data.name, data.card_type, data.text))

              // store result in the cache, under the card's name
              .do(card => this.cache[card.name] = card)
}

在组件中,您可以在需要详细信息时致电Service.getDetailedInfo()。该组件不需要自己的数据存储区。