Home > Enterprise >  How can I concat observables, but get value only from last of them?
How can I concat observables, but get value only from last of them?

Time:02-15

This is the example of the thing i have in my angular app:

concat(
  obs1$,
  obs2$,
  obs3$
)
.subscribe() // Output: obs1$ value, obs2$ value, obs3$ value. Basically normal concat behaviour

This is what i am trying to achieve:

concatLast(
  obs1$,
  obs2$,
  obs3$
)
.subscribe() // Output: obs3$ value

I don't want to use skip(), takeLast(), switchMap() end etc, cause it too much repeating code and doesn't look good when there will be like 5 obs. So i want to find universal solution. Maybe there is some RxJs operator or some tricks that allows you to do such things

CodePudding user response:

You can use the last operator to filter the last item emitted by the source observable.

const { timer, concat } = rxjs;
const { mapTo, last } = rxjs.operators

const source1$ = timer(0).pipe(mapTo(1))
const source2$ = timer(50).pipe(mapTo(2))
const source3$ = timer(100).pipe(mapTo(3))

const result$ = concat(source1$, source2$, source3$).pipe(
  last()
)

result$.subscribe(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.4.0/rxjs.umd.min.js"></script>

If you reuse this piece of code several times in your code you can avoid even more repeating code by putting this logic into a custom operator (concatLast)

const { timer, concat } = rxjs;
const { mapTo, last } = rxjs.operators

const source1$ = timer(0).pipe(mapTo(1))
const source2$ = timer(50).pipe(mapTo(2))
const source3$ = timer(100).pipe(mapTo(3))

const concatLast = (...sources) => concat(...sources).pipe(
  last()
)

const result$ = concatLast(source1$, source2$, source3$)

result$.subscribe(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.4.0/rxjs.umd.min.js"></script>

CodePudding user response:

Combinelatest will emit both a and b every time when one of them changes. a) will emit every 10sec, and b) will emit every 3 seconds.

const a = stream('a', 10000, 10, 'partial');
const b = stream('b', 3000, 10, 'partial');

combineLatest(a, b).subscribe(fullObserver('latest'));

Example: https://stackblitz.com/edit/combine-latest-flsp5m?file=main.ts

And in Angular it could be something like this

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  mapResult: any;
  from$: Subject<string> = new Subject<string>();
  to$: Subject<string> = new Subject<string>();
  searchTerm$ = combineLatest(this.from$, this.to$);

  constructor() {}
  ngOnInit() {
    this.searchTerm$
      .pipe(
        debounceTime(1000),
        switchMap((terms) => of(terms))
      )
      .subscribe((term: any) => (this.mapResult = term));
  }
}

Example: https://stackblitz.com/edit/combinelatest-angular-army-qecyev

But using of() is not recommended. So here is a bit newer example:

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  name = 'Angular Combine Lates With Interval Example';
  subOne: BehaviorSubject<number> = new BehaviorSubject(0);
  subTwo: BehaviorSubject<number> = new BehaviorSubject(0);
  counterOne: number = 0;
  counterTwo: number = 0;
  data: [number, number];

  ngOnInit() {
    setInterval(() => {
      this.subOne.next(  this.counterOne);
    }, 2000);

    setInterval(() => {
      this.subTwo.next(  this.counterTwo);
    }, 5000);

    combineLatest(this.subOne, this.subTwo).subscribe((data) => {
      console.log(data);
      this.data = data;
    });
  }
}

Example: https://stackblitz.com/edit/angular-combine-lates-with-interval-example-9vvqz7?file=src/app/app.component.ts

  • Related