Angular 2 - 异步管道中的无限循环

17

当我尝试绑定一个异步函数时,会陷入无限循环。

<tr *ngFor="let i of items">
     <td>{{myAsyncFunc(i) | async}}</td>
</tr>

这是函数:

private myAsyncFunc(i: string): Promise<string> {
        return Promise.resolve("some");
}

我做错了什么吗?还是这是一个错误?

4个回答

27

每次调用myAsyncFunc(i: string)时,您都会返回一个新的 Promise,这就是为什么会出现“无限循环”的原因。尝试返回相同的 Promise 实例 ;-)

“无限循环”实际上不是传统意义上的无限循环,而是async管道触发其输入 Promise 解决时的变更检测周期的副作用。在这个新的变更检测周期中,Angular将调用myAsyncFunc(i: string)并获取一个新的要观察的 Promise,然后解决整个问题再次开始。


1
谢谢你的回答...那么我需要在每个项目上设置Promise实例吗?还是如何返回相同的实例? - Matías González
1
你必须为每个i缓存promise(例如在map对象中),或将promise直接附加到i本身(我个人喜欢专用的视图模型)。 - Johannes Rudolph
1
@JohannesRudolph 救了我的一天 :) ! - yanivps
7
@JohannesRudolph,有没有一个简单的例子可以做这个?我还是想不出怎么做...谢谢。 - funkid
@JohannesRudolph 怎样返回同一实例? - Phani Rithvij
3
没有例子的情况下,怎么可能这就是答案呢? - El Mac

5
如果您的异步/可观察对象需要传递参数(例如,您在ngFor循环内部),也许您可以为此创建自定义异步管道。
@Pipe({
  name: 'customPipe'
})
export class customPipe implements PipeTransform {

  constructor(private someService: SomeService) {}

  /**
   * 
   * @param id 
   */
  transform(id: number): Observable<boolean> {
    return this.someService.shouldShow(id);
  }

}

在您的模板中,您可以这样调用异步管道:

<td>{{id | customPipe | async}}</td>

1

一个解决方法是使用 Memoizee

你可以按照这里的说明创建一个自定义装饰器来对函数进行记忆化处理here。之后,你只需要按照以下方式编写你的函数:

@memoize()    
private myAsyncFunc(i: string): Promise<string> {
        return Promise.resolve("some");
}

这将缓存最初返回的 Promise,当 async 管道重新检查 Promise 的状态时,它将获取缓存的 Promise 而不是一个新的 Promise。
请注意记忆函数的缓存效果。

-1

你可以查看我的博客文章,了解关于这个特定主题的内容,当它影响我们的项目并消耗了5GB的浏览器内存时 :)
链接在这里

最简单的解决方法是(如已经提到的)不要直接在模板中使用函数返回的Promise:{{ getPromise(id) | async }},而是将此Promise存储在控制器(.ts文件)中,并在视图中引用它。
此外,可以通过将变更检测设置更改为push-pull来解决此问题,但我认为这会带来更多的麻烦。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接