4
votes

I have an autocomplete HTTP call and an execute search HTTP call... I need the autocomplete call to abort when the execute action is triggered. What I am doing right now is using a Subject like so:

private onExecute$ = new Subject();

In my autocomplete method:

executeAutoComplete(e) {
  this.myService.searchAutoComplete(e.criteria)
  .pipe(
   takeUntil(this.onDestroy$),
   takeUntil(this.onExecute$),
   map( 
  // continue with more code

Then in my execute method I raise the signal:

executeSearch(e) {
console.log('triggering onExecute signal');
this.onExecute$.next();
  // other code
}

so in my console I see 'triggering onExecute signal' but the autocomplete results return AFTER the full search starts executing, I thought that once I raise the signal the takeUntil will abort the chain? It does not work.

So essentially the behavior I am trying to prevent is that the autocomplete results should not return and open the drop down when a search is triggered by the user since it's no longer relevant.

What am I missing here?

Edit -

the order is infact the issue due to the debounce time the execute is always called before autocomplete, order can be unpredictable anways. Did not realize that the onExecute.next() will be lost if the Observable chain is not yet active.

I do have a searchLoading variable that is also set, how can I use this?

I tried:

takeUntil(Observable.of(this.searchLoading))

This does not work as it's always true! how do I use takeUntil with a boolean value that is evaluated dynamically?

Edit 2 -

Got it using takeWhile instead:

takeWhile(() => !this.searchLoading))
3

3 Answers

1
votes

Could it be that executeSearch is called before executeAutoComplete? If onExecute$ has already emitted a value before the observable chain is constructed that won't stop the search from executing.

By the way: it seems to me that it would be simpler to call unsubscribe on the subscription when you run the search.

1
votes

The bad news is, it works.

I've been trying to find a flaw using this approximation of your code, but unfortunately cannot.
I post it here in case it's useful to someone.

console.clear()

const takeUntil = Rx.operators.takeUntil;

const onDestroy$ = new Rx.Subject();
const onExecute$ = new Rx.Subject();

const source$ = Rx.Observable.timer(0,1000)
const executeAt$ = Rx.Observable.timer(3000)

executeAt$
  .subscribe(x => {
     console.log('execute');
     onExecute$.next(); 
   })

source$
  .pipe(
    takeUntil(onDestroy$), 
    takeUntil(onExecute$)
  )
  .subscribe(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.2/Rx.js"></script>

On the subject of unsubscribe() vs takeUntil(), in 'Don't Unsubscribe' Ben Lesh says

Keeping too many subscription objects around is a sign you’re managing your subscriptions imperatively, and not taking advantage of the power of Rx.

and

Compose your subscription management with takeUntil


Footnote - Service Instances

Given that the Rx code looks ok, another possible cause is multiple instances of an Angular service.

If private onExecute$ = new Subject(); is wrapped in a service, and you have that service in two provide arrays (say in a module + a component, or in two components), then the instance you call .next() on may be different to the instance used in the takeUntil().

It's a long-shot, and your code above indicates it's not the case, but seems to be quite a common occurrence.

1
votes

It is old but i had the same cenario.

If you want to use takeUntil, the correct Subject is ReplaySubject.

  • Because if Subject emits anything before takeUntil call, it will not reemit when takeUntil subscribes to Subject(as implementation do), and takeUntil will not unsubscribe the source.
  • On the other way, ReplaySubject reemits on subscription, so takeUntil will cancel with it internal unsubscribe call.