Angular2:哪种是使用Observables进行多个同步调用的最佳方法? [英] Angular2: which is the best way to make multiple sync calls with Observables?

查看:965
本文介绍了Angular2:哪种是使用Observables进行多个同步调用的最佳方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在使用Promises进行服务调用之前,我正在研究Angular2/4中的Observables.

I'm studying the Observables in Angular2/4, before I've used the Promises for my services call.

我想知道进行多个同步呼叫的最佳方法是什么. 让我用一个例子来解释: 我的应用程序组件具有getUserInfo()方法,该方法需要对相互依赖的3种不同服务进行3次调用.

I'm wondering what is the best way to make multiple sync calls. Let me explain with an example: my app component have a getUserInfo() method that need to make 3 calls to 3 different services that depends on each other.

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对象,如下所示:

Now let suppose that I have an User object as the following:

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

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

I need to create a new instance of User class with the results of the 3 services call, so I need to run:

  1. getUserId();
  2. getUserPermissions();
  3. getUserInfo();

通过Observable实现这一目标的最好,最礼貌的方法是什么?

What is the best and most polite way to accomplish this through Observable?

凭诺言,我会有这样的事情:

With promises I would have something like this:

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);
    });
  })
});

推荐答案

我不能保证这是最好最礼貌的方式,因为RxJ就是这样功能强大的库,您可以通过许多不同的方法来达到相同的结果,但我会对其进行介绍.
我将提供两种选择.

I can't guarantee that this is the best or most polite way because RxJs is such a powerful library where you can achieve the same result in many different ways, but I'll give it a shot.
I will chip in two options.

假设您的服务如下所示:

Assuming your service looks something like this:

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链中可能是最简单,最干净的方法.
当然,您可以在服务方法中使用map运算符(例如.map(result => result.info))将返回类型设置为Observable<string>而不是Observable<{ info: string }>.

Notice that the methods return Observables of JSON-objects!
Since Angular http already returns Observables, it is probably the easiest and cleanest to keep it an Observable-chain all the way down.
Of course you could use the map-operator (f.e. .map(result => result.info)) inside of the service method to make the return type to Observable<string> instead of Observable<{ info: string }>.

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

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()运行之前完成...依此类推.

If you open the network-tab of your browser you will see that the requests are executed in sequence, meaning that each request has to finish before the next one starts. So getUserId() has to finish before getUserPermission() starts, which in turn has to finish before getUserInfo() can run... and so on.

您也可以改用mergeMap.唯一的区别是,当源observable发出新值时,switchMap可以取消正在进行的http请求.在此处进行比较.

You can also use mergeMap instead. The only difference is, that switchMap can cancel an ongoing http-request when a new value is emitted by the source observable. Look here for a good comparison.

这种方法允许您并行执行请求.

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));

由于forkJoin运行所有并行提供的Observable序列,因此,如果请求(或其中至少一些请求)彼此不依赖,则是更好的选择.
在此示例中,getUserId()-请求将首先运行,一旦完成,getUserPermission()getUserInfo()将开始并行运行.

Since forkJoin runs all the Observable sequences it has been given in parallel, it is the better option if the requests (or at least some of them) don't depend on each other.
In this example, the getUserId()-request will run first, and once it has finished both getUserPermission() and getUserInfo() will start running in parallel.

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

Both methods will return an object with the following structure:

{
    "id": 3,
    "name": "Bob"
    "permissions": [
        "user",
        "admin"
    ],
    "info": "is a nice person"
}

这篇关于Angular2:哪种是使用Observables进行多个同步调用的最佳方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆