【问题标题】:How to make nested Observable calls in Angular2如何在 Angular2 中进行嵌套的 Observable 调用
【发布时间】:2017-04-08 20:40:39
【问题描述】:

我在进行嵌套的 Observable 调用时遇到了一些麻烦。我的意思是调用一个检索用户的 http 服务,然后从用户那里获取 id 以进行另一个 http 调用,最后在屏幕上呈现结果。

1) HTTP GET 1 : 获取用户

2) HTTP GET 2:通过唯一标识符作为参数获取用户的偏好

这转化为组件Blah.ts中的以下代码:

版本 1 - 此代码不显示任何内容

 ngOnInit() {
        this.userService.getUser()
            .flatMap(u => {
                this.user = u; // save the user
                return Observable.of(u); // pass on the Observable
            })
            .flatMap(u => this.userService.getPreferences(this.user.username)) // get the preferences for this user
            .map(p => {
                this.preferences = p; // save the preferences
            });
    }

版本 2 - 此代码有效,但对我来说似乎是错误的方法:

 this.userService.getUser().subscribe(u => {
            this.user = u;
            this.userService.getPreferences(this.user.username).subscribe(prefs => {
                this.preferences = prefs;
            });
        });

这是模板:

<h3>User</h3>

<div class="row col-md-12">
    <div class="col-md-6">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title">User details</h3>
            </div>
            <div class="panel-body">
                <table class="table table-condensed">
                    <thead>
                        <tr>
                            <th>Username</th>
                            <th>Full Name</th>
                            <th>Enabled</th>                                
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>{{user?.username}}</td>
                            <td>{{user?.fullName}}</td>
                            <td>{{user?.enabled}}</td>                          
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    <!-- end of col 1-->

    <div class="col-md-6">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title">User preferences</h3>
            </div>
            <div class="panel-body">
                <table class="table table-condensed">
                    <thead>
                        <tr>
                            <th>Language</th>
                            <th>Locale</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>{{preferences?.preferences?.get('language')}}</td>
                            <td>{{preferences?.preferences?.get('locale')}}</td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    <!-- end of col 2-->

</div>
<!-- end of row 1-->

我认为展示服务没有任何意义,它只会让http get() 调用如下:

  http.get('http://blablah/users/')
        .map((response) => response.json())

请提出定义 Observable 链的最佳工作方法。

【问题讨论】:

  • "此代码有效,但对我来说似乎是错误的方法......" 为什么?

标签: angular rxjs angular2-observables


【解决方案1】:

你应该阅读一下 rxjs 的操作符。您的示例非常冗长,并且以不应该使用的方式使用flatMapmap。你的第一个例子也不能工作,因为你没有订阅 Observable。

这将满足您的需要:

ngOnInit() {
    this.userService.getUser().pipe(
        tap(u => this.user = u),
        flatMap(u => this.userService.getPreferences(u.username))
      ).subscribe(p => this.preferences = p);
}

旧版:

在 5.5 版本之前,rxjs 专门使用基于原型的运算符。 这段代码在功能上等同于上面的:

ngOnInit() {
    this.userService.getUser()
        .do(u => this.user = u) //.do just invokes the function. does not manipulate the stream, return value is ignored.
        .flatMap(u => this.userService.getPreferences(u.username))
        .subscribe(p => this.preferences = p);
}

【讨论】:

  • +1 - 我只有一个问题,使用此解决方案,处理任一服务调用可能引发的错误的最佳方法是什么?我知道您可以在 subscribe 方法中添加错误处理程序;但是,这只适用于 getPreferences 服务调用。在一个点上捕获两个调用的错误的最干净的方法是什么?假设它们都应该以相同的方式处理。谢谢
  • 任何错误都会一直传递下去
  • 感谢您的澄清!我现在更好地理解了这是如何工作的,谢谢。
  • .flatMap() 是否等待 .do() 发出值?
  • 如果是多个用户怎么办?
【解决方案2】:

好的,经过一天的努力和从互联网上收集信息,我学到了关于链接 Observables(按顺序调用 Observables - 一个接一个):

我正在开发一个 Angular2 (4) 网站,该网站使用 java 后端 API 来获取/设置/修改数据库中的信息。

我的问题是我必须按返回 Observables (RxJS) 的顺序进行两次 API (HTTP POST) 调用。

我有 Operation1 和 Operation2。操作 2 应在操作 1 完成后执行。

Variant1 -> 一开始我是在另一个里面做的(比如 javascript 中的嵌套函数):

     this.someService.operation1(someParameters).subscribe(
        resFromOp1 => {
          this.someService.operation2(otherParameters).subscribe(
            resFromOp2 => {
              // After the two operations are done with success
              this.refreshPageMyFunction() 
            },
            errFromOp2 => {
              console.log(errFromOp2);
            }
          );
        },
        errFromOp1 => {
          console.log(errFromOp1);
        }
      );

尽管这段代码是合法且有效的,但我需要将这些 Observable 一个接一个地链接起来,就像它是如何通过 Promises 使用异步函数完成的一样。一种方法是将 Observables 转换为 Promise。

另一种方法是使用 RxJS flatMap:

Variant2 -> 另一种方法是使用 flatMap 执行此操作,据我了解,它与 Promises 类似:

   this.someService.operation1(someParameters)
    .flatMap(u => this.someService.operation2(otherParameters))
    .subscribe(function(){
        return this.refreshPageMyFunction()
      },
      function (error) {
        console.log(error);
      }
    );

Variant3 -> 与箭头函数相同:

   this.someService.operation1(someParameters)
    .flatMap(() => this.someService.operation2(otherParameters))
    .subscribe(() => this.refreshPageMyFunction(),
      error => console.log(error)
    );

返回 Observables 的方法基本上是这些:

  operation1(someParameters): Observable<any> {
    return this.http.post('api/foo/bar', someParameters);
  }

  operation2(otherParameters): Observable<any> {
    return this.http.post('api/some/thing', otherParameters);
  }

其他资源和有用的 cmets:

This post approved answer by @j2L4e: https://stackoverflow.com/a/40803745/2979938

https://stackoverflow.com/a/34523396/2979938

https://stackoverflow.com/a/37777382/2979938

【讨论】:

  • 我喜欢这个回复,但是,大多数异步链案例都涉及将参数从 operation1 传递到 operation2,您能否将其添加到您的示例而不是 otherParameters?我完全理解您的示例,但这将使它们完整。
【解决方案3】:

第 1 版 是最好的,应该可以使用,只是您忘记订阅了:

 ngOnInit() {
    this.userService.getUser()
        .flatMap(u => {
            this.user = u; // save the user
            return Observable.of(u); // pass on the Observable
        })
        .flatMap(u => this.userService.getPreferences(this.user.username)) // get the preferences for this user
        .map(p => {
            this.preferences = p; // save the preferences
        })
        .subscribe();
}

【讨论】:

    【解决方案4】:

    你是对的,嵌套订阅是错误的......

    平面图是正确的

    这应该有帮助

    https://embed.plnkr.co/mqR9jE/preview

    或阅读本教程

    https://gist.github.com/staltz/868e7e9bc2a7b8c1f754

    一些代码...

    // responseStream: stream of JSON responses
    var responseStream = requestStream
      // We use flatMap instead of map to prevent this stream being a metastream - i.e. stream of streams
      .flatMap(requestUrl => {
        // Convert promise to stream
        return Rx.Observable.fromPromise($.getJSON(requestUrl));
      }).publish().refCount(); // Make responseStream a hot observable, prevents multiple API requests
    // see https://gist.github.com/staltz/868e7e9bc2a7b8c1f754#gistcomment-1255116
    

    这里的请求 URL 是从不同的流 / Observable 发出的输入。

    现在订阅 responseStream

    【讨论】:

      猜你喜欢
      • 2017-01-27
      • 2016-11-30
      • 1970-01-01
      • 1970-01-01
      • 2017-10-26
      • 2019-05-15
      • 2019-01-23
      • 2019-03-22
      • 1970-01-01
      相关资源
      最近更新 更多