Angular2:使用Observables进行多次同步调用的最佳方法是什么?

时间:2017-06-07 12:35:50

标签: angular angular2-services

在我将Promises用于我的服务电话之前,我正在研究Angular2 / 4中的Observables。

我想知道进行多个同步通话的最佳方法是什么。 让我用一个例子来解释一下: 我的app组件有一个getUserInfo()方法,需要对3个不同的服务进行3次调用,这些服务相互依赖。

getUserId():Number // return the logged in user userId from the session
getUserPermissions(userId):Array<String> // return a list of permission from the userId
getUserInfo(userId):String // return some additional info for the user

现在假设我有一个User对象如下:

export class User {
id: number;
name: string;
info: string;
permissions: array<string>;
}

我需要使用3个服务调用的结果创建一个User类的新实例,所以我需要运行:

  1. getUserId();
  2. getUserPermissions();
  3. getUserInfo();
  4. 通过Observable实现这一目标的最佳和最礼貌的方法是什么?

    有了承诺,我会有这样的事情:

    this._service.getUserId().then(r1 => {
      let userId: number = r1;
      this._service.getUserPermissions(userId).then(r2 => {
        let userPermissions: Array<String> = r2;
        this._service.getUserInfo(userId).then(r3 => {
          let userInfo: string = r3;
          let user: User = new User(userId, userInfo, userPermissions);
        });
      })
    });
    

1 个答案:

答案 0 :(得分:12)

我无法保证这是最佳最礼貌的方式,因为RxJs是一个功能强大的库,您可以通过多种方式实现相同的结果,但我会试一试。
我将提出两种选择。

假设您的服务看起来像这样:

<强> userservice.ts

@Injectable()
export class UserService {

  constructor(private http: Http) { }

  getUserId(): Observable<{ id: number, name: string }> {
    return Observable.of({ id: 3, name: 'Bob' }).delay(300);
    /* 
     * replace with http:
     * return this.http.get('http://my.api.com/api/user/id').map(res => res.json());
     */
  }

  getUserPermission(userId: number): Observable<{ permissions: string[] }> {
    return Observable.of({ permissions: ['user', 'admin'] }).delay(300);
    /* return this.http.get(`http://my.api.com/api/user/${userId}/permissions`).map(res => res.json()); */
  }

  getUserInfo(userId: number): Observable<{ info: string }> {
    return Observable.of({ info: 'is a nice person'}).delay(300);
    /* return this.http.get(`http://my.api.com/api/user/${userId}/info`).map(res => res.json()); */
  }
}

请注意,这些方法返回JSON对象的Observables!
由于Angular http已经返回Observables,因此将它一直保持为Observable-chain可能是最简单,最干净的。
当然,您可以使用服务方法内部的map - 运算符(f.e. .map(result => result.info))将返回类型设为Observable<string>而不是Observable<{ info: string }>

switchMap

此方法适用于必须按特定顺序发生的请求。

this.userService.getUserId()
  .switchMap(userResult =>
    this.userService.getUserPermission(userResult.id)
    .switchMap(permissionsResult =>
      this.userService.getUserInfo(userResult.id)
        .map(infoResult => ({
          id: userResult.id,
          name: userResult.name,
          permissions: permissionsResult.permissions,
          info: infoResult.info
        }))
    )
  )
  .subscribe(v => console.log('switchmap:', v));

如果您打开浏览器的网络选项卡,您将看到请求按顺序执行,这意味着每个请求必须在下一个请求开始之前完成。因此getUserId()必须在getUserPermission()开始之前完成,而getUserInfo()必须在mergeMap运行之前完成......依此类推。

您也可以使用this.userService.getUserId() .switchMap(userResult => Observable.forkJoin( [ Observable.of(userResult), this.userService.getUserPermission(userResult.id), this.userService.getUserInfo(userResult.id) ] )) .map(results => ({ id: results[0].id, name: results[0].name, permissions: results[1].permissions, info: results[2].info })) .subscribe(v => console.log('forkJoin:', v)); 代替。唯一的区别是,当source observable发出新值时,switchMap可以取消正在进行的http请求。看here进行比较。

forkJoin

此方法允许您并行执行请求。

forkJoin

由于getUserId()运行并行给出的所有Observable序列,如果请求(或者至少其中一些)不相互依赖,则它是更好的选择。
在此示例中,getUserPermission() - 请求将首先运行,一旦完成,getUserInfo(){ "id": 3, "name": "Bob" "permissions": [ "user", "admin" ], "info": "is a nice person" } 将同时开始运行。

两种方法都将返回具有以下结构的对象:

void manipulated_seive(int N)
{
    // 0 and 1 are not prime
    isprime[0] = isprime[1] = false ;

    // Fill rest of the entries
    for (long long int i=2; i<N ; i++)
    {
        // If isPrime[i] == True then i is
        // prime number
        if (isprime[i])
        {
            // put i into prime[] vector
            prime.push_back(i);

            // A prime number is its own smallest
            // prime factor
            SPF[i] = i;
        }

        // Remove all multiples of  i*prime[j] which are
        // not prime by making isPrime[i*prime[j]] = false
        // and put smallest prime factor of i*Prime[j] as prime[j]
        // [ for exp :let  i = 5 , j = 0 , prime[j] = 2 [ i*prime[j] = 10 ]
        // so smallest prime factor of '10' is '2' that is prime[j] ]
        // this loop run only one time for number which are not prime
        for (long long int j=0;
             j < (int)prime.size() &&
             i*prime[j] < N && prime[j] <= SPF[i];
             j++)
        {
            isprime[i*prime[j]]=false;

            // put smallest prime factor of i*prime[j]
            SPF[i*prime[j]] = prime[j] ;
        }
    }
}