is there an equivalent of async pipe that you can

2020-07-03 02:10发布

问题:

Does there exist something equivalent to the async pipe that I could use inside a component like this

   @Component({
      selector: 'my-component',
    })

    export class myComponent {

      myObservable$: Observable<string>;

      method() {
        doSomething(this.myObservable$);
        // here I would like to access directly the current string value of
        // myObservable$ without having to subscribe 
      }
    }

回答1:

You have to ask yourself: What do you want to achieve by avoiding the subscribe() call? My guess is that you want to prevent keeping the subscription around and having to unsubscribe manually.

If this is the case, you just have to make sure you get an Observable that completes. Then there is no need to unsubscribe later. You can convert any Observable (finite or infinite) to one that will eventually complete.

Here is an example for your case:

  method() {
    this.myObservable$.take(1).subscribe(
      val => doSomething(val)
    );
  }

The correct way really depends on what your observable does and what you want to do with the value. Angular2 http calls for example complete on their own, no need to unsubscribe!

Or if you want to call doSomething for every new value in your observable, the above solution won't fit because you only get the first value, then the observable completes. As I said, this comes down to the context of the problem.



回答2:

No there's not. You need to manually subscribe and manually unsubscribe to avoid memory leaks.

For a simple subscription you may be tempted to do :

@Component({
  selector: 'my-component',
})
export class myComponent implements OnInit, OnDestroy {
  public myObservable$: Observable<string>;
  private myObservableSub: Subscription;

  ngOnInit() {
    this.myObservableSub = this
      .myObservable$
      .subscribe(_ => {
        // do something
      });
  }

  ngOnDestroy() {
    this.myObservableSub();
  }
}

But what if you have many subscribe ? Should you do something like :

@Component({
  selector: 'my-component',
})
export class myComponent implements OnInit, OnDestroy {
  public myObservable1$: Observable<string>;
  private myObservableSub1: Subscription;

  public myObservable2$: Observable<string>;
  private myObservableSub2: Subscription;

  public myObservable3$: Observable<string>;
  private myObservableSub3: Subscription;

  ngOnInit() {
    this.myObservableSub1 = this
      .myObservable1$
      .subscribe(_ => {
        // do something
      });

    this.myObservableSub2 = this
      .myObservable2$
      .subscribe(_ => {
        // do something
      });

    this.myObservableSub3 = this
      .myObservable3$
      .subscribe(_ => {
        // do something
      });
  }

  ngOnDestroy() {
    this.myObservableSub1();
    this.myObservableSub2();
    this.myObservableSub3();
  }
}


THE ANSWER IS NO


You should rather do the following :

@Component({
  selector: 'my-component',
})
export class myComponent implements OnInit, OnDestroy {
  private componentDestroyed$ = new Subject<void>();

  public myObservable1$: Observable<string>;
  public myObservable2$: Observable<string>;
  public myObservable3$: Observable<string>;

  ngOnInit() {
    this
      .myObservable1$
      .takeUntil(componentDestroyed$)
      .subscribe(_ => {
        // do something
      });

    this
      .myObservable2$
      .takeUntil(componentDestroyed$)
      .subscribe(_ => {
        // do something
      });

    this
      .myObservable3$
      .takeUntil(componentDestroyed$)
      .subscribe(_ => {
        // do something
      });
  }

  ngOnDestroy() {
    this.componentDestroyed$.next();
    this.componentDestroyed$.complete();
  }
}

If you want to know more about that, here's an excellent article from Ben Lesh : https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87

EDIT :
Thanks to @olsn, I edited my answer and added the line with the next because indeed a complete doesn't stop the others streams.

I created a small Plunkr to demo that behaviour : https://plnkr.co/edit/dcueDqUgpqgYimxEAGUn?p=preview



回答3:

If you're in a Component or something which has access to a ChangeDetectorRef, you can do the following little trick:

@Component({
...
  providers: [AsyncPipe]
})
export class ExampleComponent {
  app$ = this.sandbox.allApps$;
  apps = this.pipeAsync.transform(this.app$);

  contructor(
    protected pipeAsync: AsyncPipe,
    protected sandbox: AppAppsSandbox
  ) {}
}

This assumes access to a service called AppAppsSandbox, but the Observable app$ can come from anywhere, this is a convenient way to unwrap it.



回答4:

The only way you can your goal (not using subscription) is to use BehaviorSubject, it has a method getValue(). There are some differences between Observables and BehaviousSubjects (you can find them in documentation).

var subject = new Rx.BehaviorSubject(current_subject_value);
subject.getValue()

Yet there are some stackoverflow debates why shoudnd't you use getValue method, but rather to use subscription... Yet there is observable take operator which will complete observable sequence (thus don't have to manage subscriptions)

this.myObservable$.take(1).subscribe()