1

The RxJS Documentation states that

combineLatest Combines multiple Observables to create an Observable whose values are calculated from the latest values of each of its input Observables.

I want to understand how combineLatest works when multiple observables emit values at the same time?

If we look at the following code

import 'zone.js/dist/zone';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';
import { timer, take, combineLatest } from 'rxjs';

@Component({
  selector: 'my-app',
  standalone: true,
  imports: [CommonModule],
  template: `Hello`,
})
export class App {
  name = 'Angular';

  constructor() {
    const a$ = timer(0, 1000).pipe(take(5)); //Emit values after each second (total 5 values)

    const b$ = timer(0, 4000).pipe(take(5)); //Emit values after every 4 seconds (total 5 values)

    //Marble Diagram
    
    //0 1 2 3 4
    //0       1       2       3       4

    const result$ = combineLatest(a$, b$);

    result$.subscribe((val) => console.log(val));
  }
}

bootstrapApplication(App);

The output is as follows Output In the output above, at the 4th second the result$ observable outputs the values [4,0] and [4,1] simultaneously.

My question is, Why it does not prints only [4,1] since the combineLatest brings the latest value and "4" is the latest value from observable a$ and "1" is the latest value from observable b$ at the 4th second.

Link to Demo

Thanks in advance!

  • To expand upon BizzyBobs good answer: since JS is single-threaded by nature, two event's can't happen at exactly the same time. Secondly, when using timers you always have some tolerance. The interval between two emissions of `timer(0,. 1000)` is never _exactly_ 1000ms, but a_at least_ 1000ms. – Lukas-T Jan 11 '23 at 06:59

2 Answers2

2

After each source emits at least one value, combineLatest emits whenever any of its source observables emit.

Even if two observables emit "at the same time" (I'm assuming you really mean in the same event loop), combineLatest will emit twice.

If you want to prevent emissions that occur in the same event loop, you could use debounceTime(0):

result$ = combineLatest(a$, b$).pipe(debounceTime(0));
BizzyBob
  • 12,309
  • 4
  • 27
  • 51
-1

You can use it like this:

import 'zone.js/dist/zone';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';
import { timer, take, withLatestFrom } from 'rxjs';

@Component({
  selector: 'my-app',
  standalone: true,
  imports: [CommonModule],
  template: `Hello`,
})
export class App {
  name = 'Angular';

  constructor() {
    const a$ = timer(0, 1000).pipe(take(5)); //Emit values after each second (total 5 values)

    const b$ = timer(0, 4000).pipe(take(5)); //Emit values after every 4 seconds (total 5 values)

    const result$ = a$.pipe(withLatestFrom(b$, (a, b) => [a, b]));

    result$.subscribe((val) => console.log(val));
  }
}

bootstrapApplication(App);

https://stackblitz.com/edit/angular-gsyobe?file=src/main.ts

Behram Bazo
  • 230
  • 2
  • 6
  • This provides a different behavior than `combineLastest`. `combineLastest` will emit when `a$` or `b$` emits. This solution only emits when `a$` emits. – BizzyBob Jan 10 '23 at 21:05
  • This doesn't really answer the question. OP asked why his code behaves as it does. Also, when posting code in an answer it's always helpful to *explain* the code, i.e. why it's better, how it solves a particular problem, etc. – Lukas-T Jan 11 '23 at 06:52
  • I second BizzyBob and churill 's thoughts. Thanks anyway for trying! – mprakhar1705 Jan 11 '23 at 16:17