ReactiveX / rxjs

A reactive programming library for JavaScript
https://rxjs.dev
Apache License 2.0
30.44k stars 2.99k forks source link

tap-complete not called for last observable inside zip #7467

Open janhommels opened 2 months ago

janhommels commented 2 months ago

Describe the bug

Zip tears down all inner observables when it completes before they can run their tap implementations. When adding a complete-function to all inner observables of a tap, the observable which will complete last and cause the whole zip to complete will not run their complete function iside their tap operator.

I understand that there are multiple solutions to this issue like using the finalize operator, however people don't expect the complete callback to not be called and it took me a while to figure that out. I'm not sure if this issues comes up in other inner/outer-observable scenarios.

Expected behavior

The complete-callback inside the tap operator should always be called, regardless of when/how it completes. In the example below, "2 completed" will not be logged. Expected behaviour would be that it will be logged.

Reproduction code

zip(
  of(1).pipe(tap({ next: n => console.log(`${n} nexted`), complete: _ => console.log("1 completed") })),
  of(2).pipe(tap({ next: n => console.log(`${n} nexted`), complete: _ => console.log("2 completed") }))
).subscribe({next: n => console.log(n), complete: _ => console.log("the end")})

Reproduction URL

https://playcode.io/1845913

Version

8.0.0-alpha.3

Environment

No response

Additional context

No response

demensky commented 2 months ago

It simply unsubscribes until complete happens. This can be clearly seen if more elements are passed to the second observable:

https://playcode.io/1846169

zip(
  of(1, 2).pipe(
    tap({
      next: n => console.log('1 nexted:', n),
      unsubscribe: () => console.log('1 unsubscribe'),
      complete: () => console.log('1 completed'),
    })
  ),
  of('a', 'b', 'c').pipe(
    tap({
      next: n => console.log('2 nexted:', n),
      unsubscribe: () => console.log('2 unsubscribe'),
      complete: () => console.log('2 completed'),
    })
  )
).subscribe({
  next: n => console.log('result', n),
  complete: () => console.log('the end'),
});