Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DelayWhen subscribe to observable when it should not #5897

Closed
BrunoBeraudPW opened this issue Nov 17, 2020 · 5 comments
Closed

DelayWhen subscribe to observable when it should not #5897

BrunoBeraudPW opened this issue Nov 17, 2020 · 5 comments

Comments

@BrunoBeraudPW
Copy link

BrunoBeraudPW commented Nov 17, 2020

Hey,
I have a snippet that shows a strange behaviour of the delaywhen operator coupled with the one of the withLatestFrom

RxJS version: 6.6.3

Code to reproduce:

import { of, Subject } from "rxjs";
import { delayWhen, withLatestFrom } from "rxjs/operators";

const source$ = of(1);
const willBeNeverEmitted$ = new Subject<number>().asObservable();
const shouldNotBeSubscribed$ = source$.pipe(
  withLatestFrom(willBeNeverEmitted$)
);

const sourceForTest$ = of(1);
sourceForTest$
  .pipe(delayWhen(() => shouldNotBeSubscribed$))
  .subscribe(() => console.log("hello"));

shouldNotBeSubscribed$.subscribe(() => console.log("never called"));

https://stackblitz.com/edit/delaywhen-bug?file=index.ts

Expected behavior:
console.log("hello") shouldn't be displayed
console.log("never called") shouldn't be displayed

Actual behavior:
console.log("hello") is display
console.log("never called") is do not displayed

Additional information:
If I change delayWhen operator by the mergeMap one, I have the expected behaviour.

@BrunoBeraudPW BrunoBeraudPW changed the title DelayWhen subscribe when observable it should not DelayWhen subscribe to observable when it should not Nov 17, 2020
@josepot
Copy link
Contributor

josepot commented Nov 17, 2020

Hi @BrunoBeraudPW

That's happening because shouldNotBeSubscribed$ synchronously completes upon subscription. The docs of delayWhen state that:

The source value is emitted on the output Observable only when the duration Observable emits a value or completes.

The behaviour that you are expecting would occur if shouldNotBeSubscribed$ didn't complete, for instance:

import { concat, of, Subject, NEVER } from "rxjs";
import { delayWhen, withLatestFrom } from "rxjs/operators";

const source$ = of(1);
const willBeNeverEmitted$ = new Subject<number>().asObservable();
const shouldNotBeSubscribed$ = concat(source$, NEVER).pipe(
  withLatestFrom(willBeNeverEmitted$)
);

const sourceForTest$ = of(1);
sourceForTest$
  .pipe(delayWhen(() => shouldNotBeSubscribed$))
  .subscribe(() => console.log("hello"));

shouldNotBeSubscribed$.subscribe(() => console.log("never called"));

@BrunoBeraudPW
Copy link
Author

Thank you @josepot for your quick answer.
So if I understood, withLatestFrom will emit a value even if no value has been emitted yet, so I agree it will subscribe synchronously.
But what I'm not understand it's, if so, why the console.log("never called") has not been called ?

@josepot
Copy link
Contributor

josepot commented Nov 17, 2020

So if I understood, withLatestFrom will emit a value even if no value has been emitted yet

That's not correct. The way that withLatestFrom works is: if the observable provided to it has emitted something before the source observable emits, then the resulting observable will emit a tuple with the latest value emitted by the source and the value emitted by the withLatestFrom observable. However, if the source emits and the observable of withLatestFrom has not emitted, then the resulting observable won't emit anything (because its missing the value from the withLatestFrom observable). In either case, whenever the source completes, the resulting observable will also complete. Therefore of(1).pipe(withLatestFrom(NEVER)) behaves exactly like EMPTY, meaning that the resulting observable is an observable that synchronously completes without emitting anything upon subscription.

I hope that this helps.

If I'm not mistaken, a poor's man implementation of withLatestFrom would be something like this:

let emptyValue: any = {}
             
const withLatestFrom = <L, T>(latest$: Observable<L>) => (
  source$: Observable<T>,                                 
) =>                                                      
  new Observable<[T, L]>((subscriber) => {                
    let latestValue: L = emptyValue                       
    return latest$                                        
      .subscribe((value) => {                             
        latestValue = value                               
      })                                                  
      .add(                                               
        source$.subscribe(                                
          (value) => {                                    
            if (latestValue !== emptyValue)               
              subscriber.next([value, latestValue])       
          },                                              
          (e) => subscriber.error(e),                     
          () => subscriber.complete(),                    
        ),                                                
      )                                                   
  })                                                      

@benlesh
Copy link
Member

benlesh commented Nov 17, 2020

FYI, @josepot @BrunoBeraudPW we have fixed the behavior of delayWhen in version 7. It only next notifications will trigger the delay callback starting in version 7... We've finally made the library consistent in that way.

See #3665 and #5769

@benlesh benlesh closed this as completed Nov 17, 2020
@BrunoBeraudPW
Copy link
Author

@josepot Understood ! Thank !
@benlesh Oh okay, good to know, thank for the answer too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants