Search code examples
angularrxjsrxjs-observables

Angular Async Pipe and the 'delay' operator


I'm attempting to simulate latency with my observables.

I want to unwrap the observable using the async pipe, and add a bit of delay with the 'delay' operator.

app.component.html

<ul *ngIf="items$ | async as items">
   <li *ngFor=let item of items">{{ item }}<li>
</ul>

app.component.ts

get items$(): Observable<string[]> {
    return of(['alpha', 'bravo', 'charlie', 'delta']).pipe(delay(3000));
}

However, doing it in this fashion returns no HTML markup. Removing the pipe(delay(3000)) allows it to work.

If I implement 'OnInit' and just check on the observable:

ngOnInit(): void {
   this.items$.subscribe(val => console.log(val));
}

In three seconds the console will output:

(4) ["alpha", "bravo", "charlie", "delta"]

So the observable is behaving like I want it to, but it seems I am not utilizing the async pipe correctly.

What am I missing about about how the async pipe works? How do I simulate this delay in a simple fashion?


Solution

  • You need a single Observable instance. Your code currently will create and return a new Observable every time your property items$ is accessed. Create it one time instead. You could still use a getter if you wanted to as long as it is the same Observable instance being returned with each call.

    items$: Observable<string[]>;
    constructor() {
      this.items$ =  of(['alpha', 'bravo', 'charlie', 'delta']).pipe(delay(3000));
    }
    

    or

    private _items$?: Observable<string[]>;
    get items$(): Observable<string[]> {
      if (!this._items$) {
        this._items$ =  of(['alpha', 'bravo', 'charlie', 'delta']).pipe(delay(3000));
      }
      return this._items$;
    }
    

    If you want to see how/why your original code is failing you could add a counter in the getter and then you can see the number of Observable being created and returned.

      numberOfCalls = 0;
      get items$(): Observable<string[]> {
        this.numberOfCalls++;
        return of(['alpha', 'bravo', 'charlie', 'delta']).pipe(delay(3000));
      }
    

    and then add this to your template

    calls to items$ which create and return a new observable = {{numberOfCalls}}