Angular2:使用 Observables 进行多次同步调用的最佳方式是什么? [英] Angular2: which is the best way to make multiple sync calls with Observables?

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

问题描述

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

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

推荐答案

我不能保证这是最好最礼貌的方式,因为 RxJs 就是这样一个强大的库,您可以通过多种不同的方式实现相同的结果,但我会试一试.
我将提供两个选项.

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 对象的 Observable!
由于 Angular http 已经返回 Observables,因此将其一直保持为 Observable-chain 可能是最简单和最干净的.
当然,您可以在服务方法中使用 map-operator (fe .map(result => result.info)) 将返回类型设为 Observable 而不是 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天全站免登陆