在订阅

时间:2017-10-27 06:27:16

标签: javascript angular rxjs

我正在开发一个4.4.4角应用程序,商家可以向客户赠送积分;为此,我给了httpclient srevice调用(为此我使用cancateMap虽然不知道该使用什么)。

我有多个商家,所有人都可以给用户点数,所以我想按顺序为每个商家(一个接一个)调用httpClient req。作为回应我想显示点数增加,如计数器和加载栏,同时添加点数。例如**

  

如果星巴克增加20分,那么在回应视图中应该显示积分   递增到20,并且将显示行列式加载器直到它   完成100%。我想为每个请求显示此信息

对于装载机我使用材料决定性装载机 **: 以下是我的组件代码

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { SharedService } from '../services/shared.service';
import { CommonService } from '../services/common.service';
import { Observable } from 'rxjs/Rx';
@Component({
  selector: 'app-dashboard',
  templateUrl: './onboarding.component.html',
  styleUrls: ['./onboarding.component.scss']
})
export class DashboardComponent implements OnInit {
  public merchantsList: Object[];
  public points: number = 0;
  public counter: number = 0;
  public merchantName: string;
  public percentage: number;
  constructor(private router: Router, private commonService: CommonService, private sharedService: SharedService) { }

  ngOnInit() {
    this.merchantsList = this.sharedService.getMerchants();
    this.transferPoints();

  }

  transferPoints() {
    let data = {
      "points": 20,
      "issueToPartyName": "O=User1,L=New York,C=US",
      "issuerBankPartyRef": "1"
    };

    Observable.of(...this.merchantsList)
      .concatMap((merchant: Object) => {
        return this.commonService.getAllMerchatPoints(merchant, data).map((res) => {
          return res
        });
      }).subscribe((res) => {

        let timer = Observable.interval(200).subscribe(() => {
          this.merchantName = res.initiator;
          this.points++;
          this.counter++;
          this.percentage = (this.counter / 20) * 100;
          if (this.percentage === 100) {
            timer.unsubscribe();
            this.percentage = 0;
            this.counter = 0;
          }
        });
      });
  }
}

transferPoints()是我调用的方法,使用concateMap给出顺序http调用,然后在其订阅中我获得积分转移和商家名称:

HTML是:

<div class="mobile-img">
  <div class="home-card flex-container flex-col-direction flex-center-center">
    <div>
      <img class="icon" src="assets/images/user-big.png" />
    </div>
    <div>
      <div class="amount">{{points}}</div>
      <div class="subtext">Points</div>
    </div>

    <div class="state">Fetching your reward points...</div>

    <div class="vendor">{{merchantName|| 'Uber'}}</div>

    <mat-progress-bar mode="determinate" value="{{percentage}}"></mat-progress-bar>
  </div>
</div>

和服务代码是:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHandler, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { LoaderService } from './loader.service';
interface NodeResponse {
  name: string;
  nodeName: string;
  nodeType: string;
  port: number;
  initiator?:string;
}


@Injectable()
export class CommonService extends HttpClient {
  private contextPath = "url";
  constructor(private http: HttpClient, public httpHandler: HttpHandler, public loaderService: LoaderService) {

  }
  getAllNodes(): Observable<NodeResponse[]> {
    return this.http.get<NodeResponse[]>(`${this.contextPath}:10007/api/transferpoints/getAllNodesInfo`);
  }

  getAllMerchatPoints(merchant, data): Observable<NodeResponse> {
   return this.http.put<NodeResponse>(`${this.contextPath}:${merchant.port}/api/transferpoints/issueandtransfer`, data);
  }
}

getAllMerchatPoints是我正在调用的服务

问题是我无法顺序从每个请求中获取数据。我的意思是当与星巴克相关的http req完成时,我想增加点并显示像其取点一样的加载;在完成星巴克需求之后,优步应该开火并进一步想要增加积分并显示像其取点一样的加载。

我在下面尝试过订阅;使用间隔,因为我想显示加载器移动到结束;但它不能正常工作。

    let timer = Observable.interval(200).subscribe(() => {
              this.merchantName = res.initiator;
              this.points++;
              this.counter++;
              this.percentage = (this.counter / 20) * 100;
              if (this.percentage === 100) {
                timer.unsubscribe();
                this.percentage = 0;
                this.counter = 0;
      }
   });

我使用正确的操作员吗? concateMap?有没有其他方法可以实现上述目标?

1 个答案:

答案 0 :(得分:1)

映射运算符

在您的应用中使用concatMap vs flatMap取决于您是否要保留商家的订单。
记住http是异步的,因此有些呼叫可能比其他呼叫响应更快。 concatMap保留原始排序,而flatMap会在响应到达后立即发出。

否则,他们都做同样的工作 - 解开内在的观察者。

计时器

虽然技术上定时器代码没有任何问题,但我认为这让图片混乱。基本上,http响应不会顺利到达,所以除非你知道响应总是花费不到200毫秒,否则你不能指望这个定时器以200毫秒的间隔发出。

Observable.of(... this.merchantsList).concatMap(

的结果
  

无法顺序从每个请求中获取数据

您解释了您想要获得的内容,但不是您 获得的内容。我不明白为什么你不会得到顺序结果,特别是使用concatMap

尝试注释掉计时器代码并输入console.log(res)以查看您获得的回复顺序。

  

..所以两个间隔同时运行,因为它们是异步的!   想知道有没有办法顺序运行间隔

所以,我认为外部订阅中的这个块

let timer = Observable.interval(200).subscribe(() => {
  ...
}

快速运行并返回下一个商家回复。内部订阅独立运行(正如您所说的异步)。没有那个间隔包装器,内部部件将同步运行,一切都将按顺序进行。但我想你想要一个小的延迟来获得正确的屏幕效果。

也许你需要在订阅之外而不是在内部的时间间隔,比如这个

}).delay(200).subscribe((res) => { 

使用combineLatest()

这是我迄今为止采用这种方法的原因。

const merchantPoints$ = Observable.of(...this.merchantsList)
  .concatMap((merchant: Object) => {
    return this.commonService.getAllMerchatPoints(merchant, data).map((res) => {
      return res
    });
  });
const timer$ = Observable.interval(200);  
Observable.combineLatest(merchantPoints$, timer$)
  .subscribe(combined => {
    const res = combined[0];
    this.merchantName = res.initiator;
    this.points++;
    this.counter++;
    this.percentage = (this.counter / 20) * 100;
    if (this.percentage === 100) {
    //   timer.unsubscribe();
      this.percentage = 0;
      this.counter = 0;
    }
  });