angularrxjsrxjs-pipeable-operators

takeUntil() after a filter() behaves differently from take(n) in pipe


Edit(26th September) :

this.activatedRoute.queryParamMap
      .pipe(
        // activatedRoute.queryParamMap emits null values first. So let's avoid it
        filter((paramMap) => paramMap && paramMap.keys.length > 0),
        tap((value) => console.log("tap", value)),
        // unsubscribe immediately if some params are found
        take(1),
        // If the params are really empty, filter will never pass. So let's unsubscribe after sometime
        takeUntil(timer(1000)),
        finalize(()=>console.log("finalize")),
        delay(1),
        combineLatestWith(this.sortableColumns$),
      )
      .subscribe(([paramMap, sortableColumns]) => {
        console.log('paramMap');
        this.setTableOptionsFromUrl(paramMap, sortableColumns);
      });

With the above code and a sample url of http://localhost:4205/inloggen (without query params)

This is the output

enter image description here

So why the message in tap didn't get logged but the "finalize" got printed to the console after 1 second.

So I would like to know what operators like (take, tap, delay, combineLatestWith etc.) obeys filter and what doesn't obey (takeUntil, finalize etc.)


      

      


Original:

this.activatedRoute.queryParamMap
      .pipe(
        // activatedRoute.queryParamMap emits null values first. So let's avoid it
        filter((paramMap) => paramMap && paramMap.keys.length > 0),
        // unsubscribe immediately if some params are found
        take(1),
        // If the params are really empty, filter will never pass. So let's unsubscribe after sometime
        takeUntil(timer(1000)),
        delay(1),
        combineLatestWith(this.sortableColumns$),
      )
      .subscribe(([paramMap, sortableColumns]) => {
        console.log("paramMap")
        this.setTableOptionsFromUrl(paramMap, sortableColumns);
      });

I have the above code.

My question is,


Solution

  • If I understand correctly, what u r confused about is that takeUntil should not trigger unless something above it has emitted a value. But it is actually intended that takeUntil will trigger regardless if it receive any emits.

    Explanation:
    Unlike most operators which accepts a value from the pipe chain and then performs callbacks using the value received, takeUntil does not accept any value passed from the pipe.

    I want to set up a scenario to help explain:

    notifier$     = timer(1000)
    queryParam$   = this.activatedRoute.queryParamMap
      .pipe(
         filter((paramMap) => paramMap && paramMap.keys.length > 0),
         take(1),
         takeUntil(this.notifier$),
      )
    

    when u subscribed to queryParam$, what rxjs does internally is, it will also subscribe to notfier$ in parellel with queryParam$, this is regardless if queryParam$ emits anything at all

    parellel subscription


    maybe u might still be confused why does rxjs does it this way, so I would like to show u another use case of takeUntil.

    notifier$     = fromEvent(stopButton, 'click')
    oneHourTimer$ = timer(1000 * 60 * 60).pipe(
      takeUntil(this.notifier$)
    )
    

    now in this second scenario, it is possible for user to click the stopButton before the oneHourTimer$ has even emit anything at all. So in short, by subscribing both observable at the same time, takeUntil does not care if there is any value that is being passed down the pipe.


    as for finalize, following the official rxjs Doc, I think its behaviour is quite straight forward. official rxjs explanation of finalize

    In ur case, the queryParamMap observable is terminated by takeUntil, thus it triggers finalize