【问题标题】:Angular2: Nested subscribe with second subscribe inside the loopAngular2:嵌套订阅与循环内的第二个订阅
【发布时间】:2017-08-04 22:07:06
【问题描述】:

我刚刚使用 Angular2 一周大!

基本上有 2 个 API 调用。

  1. 第一个 API 调用给我一个 json 对象的数组 (queryResults)。 (第一次订阅)
  2. 在视图中显示该数组。
  3. 遍历 (queryResults) 数组并选择一个特定的键:每个数组元素中的值对 (queryResults[i].oldLastSeen)。
  4. 将该值 (oldLastSeen) 作为请求参数传递给另一个 API,从而得到一个字符串 (newLastSeen)。 (第二次订阅)
  5. 在 queryResult 中将 oldLastSeen 替换为 newLastSeen

由于 Observable 的异步特性,问题出现在第 5 步。我无法访问内部订阅内的queryResults[i].oldLastSeen,基本上是未定义的。因此 lastSeen 不会更新。

首先调用getLastSeen(), 代码如下所示:

temp: any[];
rows: any[];

getLastSeen() {
    this._someService.getQueryResults(this.query)
      .subscribe(queryResults => {
        this.rows = queryResults;
        this.updateLastSeen(this.rows);
      });
}

private updateLastSeen(rows) {
    this.temp = rows; //temp: any[];
    for(var i=0; i<this.temp.length; i++) {
        if(this.temp[i].id != null || this.temp[i].oldLastSeen != null) {
            //Some operations here
            this._someService.getNewLastSeen(this.temp[i].oldLastSeen)
                .subscribe(
                    newLastSeen => {
                        this.rows[i].oldLastSeen = newLastSeen; //This doesnot happen!
                    },
                    error => alert(error),
                    () => console.log("Finished.")
                );
        }
    }
  }

从 2 天以来一直在敲我的头,遇到了 flatMap、switchMap 等。但对它们的使用高度怀疑。

更新:工作代码:

我按照 Bryan 的反馈,对他建议的代码进行了一些修改。这对我有用:

this._someService.getQueryResults(this.query)
      .do(qr => {this.rows = qr;})
      .flatMap(queryResults => Observable.from(queryResults))
      .filter(queryItem => queryItem["id"] != null || queryItem["oldLastSeen"] != undefined)
      .do(queryItem => { // did some operations/ manipulations here})
      .mergeMap(queryItem => this._someService.getNewLastSeen(this.queryItem["oldLastSeen]),
           (queryItem, newLastSeen) => [queryItem, newLastSeen])
      .subscribe(([queryItem, newLastSeen]) => {
        queryItem.oldLastSeen = newLastSeen
      }); 

【问题讨论】:

  • 是在等待用户的输入还是发生了什么??
  • getLastSeen() 从 ngOnInit() 调用。没有用户输入。

标签: angular http request angular2-observables angular2-http


【解决方案1】:

一般来说,如果您有嵌套订阅,则您正在处理一种可以改进的反模式。我还没有找到一个令人信服的案例来使用它。我不确定对 flatMap 或 switchMap 持怀疑态度意味着什么,但它们是非常有用的运算符,尤其是 switchMap。如果你的工具包中没有 switchMap,你将无法做超出 rxjs 基础的很多事情。

getLastSeen() {
    this._someService.getQueryResults(this.query)
      .do(qr => this.rows = qr;)
      .flatMap(queryResults => Observable.from(queryResults))
      .filter(queryItem => queryItem.id !== null || queryItem.oldLastSeen !== null)
      // you can insert a map or do operator here to accomplish the operations you mention, depending on what they are, or you can possibly just do them inside switchMap
      .switchMap(queryItem => this._someService.getNewLastSeen(queryItem.oldLastSeen),
           (queryItem, newLastSeen) => [queryItem, newLastSeen])
      .subscribe(([queryItem, newLastSeen]) => queryItem.oldLastSeen = newLastSeen);

}

一块一块地看:

  1. 你得到你的查询

  2. 您将结果设置为视图。功能纯粹主义者会说在这里这样做是不好的做法,因为它使用了副作用,但无论如何。我不是纯粹主义者。

  3. 这很棘手,但是您将结果扁平化为可观察的流。您从结果数组创建一个可观察对象,然后 flatmap 将它们一一发出,您也可以在这里使用 .mergeAll() 。这是一种更被动的结果循环方式。

  4. 过滤掉你不感兴趣的项目

  5. “操作”的占位符行,可能是 map 或 do,很难在不知道所需效果的情况下说。也可能只是在 switchMap 中可行,但要注意样式并保持运算符简洁。

  6. switchMap 到新的 observable 中,switchMap 将订阅内部 observable 并发出结果,关键是 switchMap 的第二个参数,它允许您组合来自内部和外部 observable 的项目。这里我们只是将它们放入一个数组并发出两个值的数组

  7. 重置订阅中的项目。这里唯一有趣的是使用打字稿语法,它可以让你命名数组中的项目,因为你知道你得到了一个包含 2 个项目的数组。

【讨论】:

  • 太棒了!这是有效的,但有一些修改。 switchMap 正在削减前一个流,因此只有一个 queryItem 的最后一次被转换。所以我改用mergeMap。编译错误也很少,但总而言之,这是一个可行的概念。谢谢!
  • 很高兴听到这个消息。是的,我并不感到惊讶,因为我只是在响应框中尽可能地写了一些错误。始终努力避免嵌套订阅,几乎总是有更好的方法。最佳实践是函数返回冷的 observables,函数调用者负责订阅。这确实有助于避免内存泄漏,因为糟糕的订阅管理很容易造成大量内存泄漏。
  • 嘿,布莱恩,如果我想为第二次通话做条件流怎么办。我的意思是我只想在某些条件通过时才调用第二个 API。我可以把那个 if 语句放在哪里?
  • 如果你不想对它们做任何事情,只需像你已经一样过滤掉它们。如果您想有条件地进行异步/同步转换,那么在 mergeMap(或 switchMap)内部执行类似 .mergeMap(item => { if (condition) { return asyncTransform(item) } else { return Observable.of(syncTransform(item )) } }) 其中异步转换返回一个 observable。如果您希望所有项目都通过流处理,但如果不是条件则不执行任何操作,则只需返回 Observable.of(item)。完全有效的技术。您还可以使用相同的设置有条件地应用不同的异步转换
猜你喜欢
  • 2020-10-17
  • 2020-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-27
  • 2021-01-10
  • 2021-11-18
  • 2022-01-24
相关资源
最近更新 更多