简体   繁体   中英

Combine RxJS observables with first operator

The question might no be the best cause I'm new to rxjs. I apologize in advance.

 this.route.queryParams.pipe(takeUntil(this.destroy$)).subscribe((queryParams) => {
      this.one();
      this.two();
    });

    this.route.queryParams.pipe(first(), takeUntil(this.destroy$)).subscribe((queryParams) => {
      this.three();
    });

Is it possible to combine these two observables into one? this.three needs to be fired only once, as first operator is used.

You can use combineLatest to combine observables that way if you need emitted value to do some control :

combineLatest([observable1$, observable2$]).subscribe(([val1, val2]) => {})

When any of your observables emits a value combineLatest will emit the last value of each, or you can use merge to get a single observable

You could use a merge to do it:

const queryParams$ = this.route.queryParams.pipe(share());

const executeOthers$ = queryParams$.pipe(
  tap(() => {
   this.one();
   this.two();
  })
);
const executeThree$ =  queryParams$.pipe(
  first(),
  tap(() => this.three())
);

merge(executeOthers$, executeThree$).pipe(
    takeUntil(this.destroy$)
).subscribe();

By doing so, you make sure that the three() method get executed on the first queryParams emission. The two other methods (one and two) will be executed on first and each time you change your route params.

The cleanest solution is likely to just subscribe twice.

If you do prefer to combine the two, I would do it like this:

this.route.queryParams.pipe(
  takeUntil(this.destroy$),
  map((value, index) => ({value, index})),
).subscribe(({value: queryParams, index}) => {
  if(index === 0){
    this.three();
  }
  this.one();
  this.two();
});

This adds an index right at the end of your stream, so you can know how many values have been emitted and use it to perform some logic.


Another solution is to basically tap the first value. Since tap doesn't give us an index, we can use map and pass values through unaltered.

this.route.queryParams.pipe(
  takeUntil(this.destroy$),
  map((value, index) => {
    if(index === 0) this.three();
    return value;
  }),
).subscribe(queryParams => {
  this.one();
  this.two();
});

Another solution is to create a boolean that tracks if you're on the first value in a stream or not. To do this, you're essentially required to create a custom operator.

this.route.queryParams.pipe(
  takeUntil(this.destroy$),
  o => defer(() => {
    let first = true;
    return o.pipe(
      tap(val => {
        if(first){
          this.three();
          first = false;
        }
      })
    )
  }),
).subscribe(queryParams => {
  this.one();
  this.two();
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM