19

I`m getting an endless loop when I try to bind a async function like this:

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

this is the function:

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

I'm doing something wrong? Or this is a bug?

4 Answers 4

29

You're returning a new Promise from myAsyncFunc(i: string) on every call, that's why you get an "endless loop". Try returning the same Promise instance ;-)

The "endless loop" is actually not a traditional endless loop but rather a side-effect of async pipe triggering a change detection cycle when its input Promise resolves. On this new change detection cycle, angular will call myAsyncFunc(i: string) and get a new Promise to observe, which then resolves the whole thing starts again.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for the answer... So I have to set the Promise instance on each item? Or how I return the same instance?
You would either have to cache the promise for each i (e.g. in a map object) or simply attach the promise to i itself (I personally like having dedicated view models anyway).
@JohannesRudolph Saved my day :) !
@JohannesRudolph Is there a simple example for doing this? Still can not figure out how to do this...thanks
How can this be the answer when there is no example.
|
5

If your async/observable requires you to pass a parameter (e.g., you are inside an ngFor loop) perhaps you can create a custom async pipe for that.

@Pipe({
  name: 'customPipe'
})
export class customPipe implements PipeTransform {

  constructor(private someService: SomeService) {}

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

}

And in your template you can call your async pipe as:

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

Comments

1

A workaround would be to use Memoizee.

You can create a custom decorator to memoize your function as instructed here. After that you can just write your function as follows:

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

This caches the initially returned Promise and when the async pipe re-checks the Promise's status it will get the cached Promise not a new one.

Be aware of the caching effects of memoizing a function.

Comments

-1

You can check my blogpost on this specific topic, when it strikes our project consuming 5GB od browser RAM :)
It's here

Simplest way to heal this issue is (as already mentioned) don't use function returned promise direct in template: {{ getPromise(id) | async }} but store this promise in controller (.ts file) and refer to it in view.
In addition, this can be healed by changing change detection settings to push-pull, but in my opinion it's brings way more evil than good.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.