【问题标题】:How to delay an observable subscription to finish method execution from first observable如何延迟可观察订阅以完成从第一个可观察的方法执行
【发布时间】:2017-04-23 23:54:39
【问题描述】:

在我的 angular2 应用程序中,我想按顺序执行可观察订阅,但我收到错误,因为第一个订阅有一个我认为需要很多时间的方法。我为不同的组件提供服务,以便它们缓存我的数据并可供所有组件使用。我是 Http Observables 的新手,不知道它们执行的顺序。

PriceList.service.ts

export class PriceListService
{
    private priceLists : PriceList[];
    private observable : Observable<any>;
    public getAll()
    {
        if(this.priceLists)
        {
            return Observable.of(this.priceLists);
        }
        else if (this.observable)
        {
            return this.observable;
        }
        else
        {
            this.observable = this.authHttp.get(this.appService.getApiUrl() + "api/price-list/list")
                .map(response => {
                    this.observable = null;
                    if(response.status == 400)
                    {
                        return "Failure";
                    }
                    else if(response.status == 200)
                    {
                        let myJson = response.json();
                        this.priceLists = myJson.data.priceList;
                        return this.priceLists;
                    }
                })
                .share();
            return this.observable;
        }
    }
}

Product.service.ts

export class ProductService
{
    products : Product[];
    observable : Observable<any>;
    priceLists : any;
    priceListMap : Map<number, any> = new Map<number, any>();
    defaultPriceList : Map<number, any> = new Map<number, any>();
    productMap : Map<number , any> = new Map<number , any>();
    defaultPriceListId : number;
    public getActiveProducts()
    {
        if(this.products)
        {
            return Observable.of(this.products);
        }
        else if (this.observable)
        {
            return this.observable;
        }
        else
        {
            this.observable = this.authHttp.get(this.appService.getApiUrl() + "api/product/list/active")
                .map(response => {
                    this.observable = null;
                    if(response.status == 400)
                    {
                        return "Failure";
                    }
                    else if(response.status == 200)
                    {
                        let myJson = response.json();
                        this.products = myJson.data.products;
                        this.getPriceLists();
                        return this.products;
                    }
                })
                .share();
            return this.observable;
        }
    }
    getPriceLists()
    {
        this.priceListService.getAll().subscribe(
            success => { this.priceLists = success; },
            error => {},
            ()=> { this.populatePriceListMap(); }
        );
    }
    populatePriceListMap()
    {
        for(let priceList of this.priceLists)
        {
            for(let product of this.products)
            {
                for(let prices of product.prices)
                {
                    if(priceList.id == prices.priceList.id)
                    {
                        this.productMap.set(product.id , {price : prices.price , discount : prices.discount});
                    }
                }
            }
            if(priceList.isDefault == 1)
            {
                this.defaultPriceList.set(priceList.id , this.productMap);
                this.defaultPriceListId = priceList.id;
            }
            this.priceListMap.set(priceList.id , this.productMap);
            this.productMap = new Map<number , any>();
        }
    }
    getDefaultList()
    {
        if(this.defaultPriceList){
            return Observable.of(this.defaultPriceList.get(this.defaultPriceListId));
        }
    }
}

ListProduct.component.ts

export class ListProductsComponent implements OnInit
{
    products:any = null;
    pricesMap : Map<number, any> = new Map<number, any>();
    prices:any = [];
    discounts:any = [];
    ngOnInit()
    { 
        this.productService.getActiveProducts().subscribe(
            success =>{
                this.products = success;
            },
            error =>{},
            () =>{
                this.populatePrices();
            }
        );
    }
    populatePrices()
    {
        this.productService.getDefaultList().subscribe(
            success =>
            {
                this.pricesMap = success;
            },
            error =>{},
            ()=>{
                for (let product of this.products)
                {
                    let myObject = this.pricesMap.get(product.id);
                    this.prices[product.id] = myObject.price;
                    this.discounts[product.id] = myObject.discount;
                }
            }
        );
    }
}

错误

应该怎么做

我希望我的组件按此顺序执行功能。

  1. 我想执行 getActiveProducts() 以便我的 productService 从 api 获取产品并存储它们。

  2. 我想执行getPriceLists() of productService 以从其他服务获取数据并将其存储在productService 中。

  3. 我想执行populatePriceListMap(),以便它填充不同的服务属性。

  4. 我想在我的组件中执行populatePrices(),并为不同的属性赋值。

我面临的问题

我认为它在第 1 步完成工作时执行第 1 步和第 4 步。我想显示在步骤 4 中设置的属性,但它没有分配它们并给出错误。

【问题讨论】:

    标签: angular typescript rxjs observable


    【解决方案1】:

    您在订阅的onNext 回调中设置此pricesMap 变量,然后在onCompleted 回调中访问它。 您显然将其设置为一个未定义的值,该值作为一个值传入。这就是错误。

    至于最佳实践,您可能想看看如何做 Rx,in an Rx-like way

    【讨论】:

      【解决方案2】:

      如果您可以并行进行两个 get 调用(一个调用不依赖于另一个,只需要同步结果),那么我建议使用 forkJoin 运算符。它用于处理并行调用,等待它们全部完成,然后再为每个调用输出结果。

      例如: 编辑: 之前有一个错误:我把 take(2) 放在第二个 observable 上,而它应该是 take(1)

      var obs1 = this.authHttp.get(firstUrl).take(1);
      var obs2 = this.authHttp.get(secondUrl).take(1);
      Observable.forkJoin(obs1, obs2)
                .subscribe(responseArray => {
                     // Do something 
                     // where responseArray[0] is the response you receive from the first get call, obs1
                     // responseArray[1] is the response you receive from the second get call, obs2
                });
      

      您可以在此处阅读更多信息:https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/forkjoin.md

      但是,如果您想按顺序运行(第二个 get 调用需要来自第一个 get 响应的参数),那么您正在寻找 switchMap 运算符。所以使用上面相同的 obs1 和 obs2,你会想要这样做:

      obs1.do(resp1 => {
             // Anything you want to do with the first response before you fire off the second one
        }).switchMap(obs2)
          .subscribe(resp2 => {
                     // Do something 
          });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-24
        • 1970-01-01
        • 2017-01-01
        • 1970-01-01
        • 2019-06-21
        相关资源
        最近更新 更多