Home > Enterprise >  How to iterate array of Observable<boolean> and break when an Observable emits certain value?
How to iterate array of Observable<boolean> and break when an Observable emits certain value?

Time:01-18

I now have a type:

export interface IClickHandler {
  index: number; // call order starting from 0
  handleClick$(event: any): Observable<boolean>;
}

Before the handleClick$ looked like this:

handleClick(event: any): boolean;

So in each handler I created a new method handleClick$ that calls the old handleClick:

  public handleClick$(evt: any): Observable<boolean> {
    return new Observable(subscriber => {
      const isHandled = this.handleClick(evt);
      subscriber.next(isHandled);
      subscriber.complete();
    });
  }

in my component i have an array-field and a method seen below:

private handlers: IClickHandler[] = [];

I had method that executed each handler.handleClick until one returns true.

  public handleControlClick(event: any) {
    // these are ordered in array to ensure call order
    for (const handler of this.handlers) {
      if (handler.handleClick(event)) {
        break;
      }
    }
  }

And now I need to adapt it to:

boolean -> Observable<boolean>

change. How this is done?

CodePudding user response:

You can try creating an array of all your observables, and then using merge, you will get an observable that emits whenever each individual observable emits a value, and using takeWhile with your condition to unsubscribe once it becomes false, something like this

import { takeWhile, interval, tap, merge } from 'rxjs';

const s1 = interval(1000);
const s2 = interval(2000);
const s3 = interval(3000);

const arr = [s1, s2, s3];
const stoppingValue = 7;

const c = merge(...arr).pipe(
  tap((value) => console.log(value)),
  takeWhile((value) => value !== stoppingValue),
).subscribe();

CodePudding user response:

One option would be to leverage concat and every in combination to concatenate your observables and shortcircuit if one of the conditions fails (in this case if one of the obsevables returns true):

Setup

const handlerObjects: IClickHandler[] = [
  {
    index: 0,
    handleClick$: of(false)
  },
  {
    index: 1,
    handleClick$: of(false)
  },
  {
    index: 2,
    handleClick$: of(true)
  },
  {
    index: 3,
    handleClick$: of(false)
  },
];

Logic

const handlers: Observable<boolean>[] =
  handlerObjects
    .map(
      (handlerObject, index) => handlerObject.handleClick$
         // Unnecessary pipe, just for index tracking
         .pipe(tap(() => {console.log(`${index} called`)
    );

const concatHandlers$: Observable<boolean> = concat(...handlers).pipe(every((value) => !value))

concatHandlers$.subscribe();

// 0 called
// 1 called
// 2 called

// Note: 3 not called due to shortcircuit on the 3rd item returning true

CodePudding user response:

I read from the comment

IF the handler emits true the next handler's code should not be executed. Each handleClick$ emits only 1 value and completes

So, I understand that you want to run the handler of the first Observable that emits and not run any other handler after that.

If this understanding is right, then one way could be the following

merge(...this.handlers)
  .pipe(take(1))
  .subscribe();
  • Related