Зависими вложени наблюдаеми

Имам два метода, в които извършвам някаква асинхронна операция, но не искам метод 2 да се изпълнява, докато или освен ако отговорът от метод 1 не е положителен, за да покаже, че можем да продължим по-нататък.

И така, това е, което опитах:

Метод 1:

private method1(): Observable<any> {
  return new Observable(() => {
    executingSomething();
    this.anotherSubscription.pipe(
      map(x => {
        console.log('Control is not reaching here');
        Also, how to indicate the caller that method2 can be executed?
        I can't return anything since I'm already returning the new Observable, above.
      })
    );
  });
}

Обаждащ се:

concat(this.method1(), this.method2()).subscribe();

Проблем: anotherSubscription дори не се изпълнява и не мога да измисля никакъв начин да предам отговор от anotherSubscription на повикващия.

Чувствам, че не използвам наблюдаемите в правилния смисъл тук, но изглежда не мога да намеря нищо никъде.


person Suyash Gupta    schedule 23.12.2020    source източник
comment
Това също е тясно свързано: stackoverflow .com/questions/50452947/.   -  person Avius    schedule 23.12.2020


Отговори (3)


Направих някои предположения относно вашия код, преди да напиша решението. Ако предположенията са грешни, тогава отговорът също може да е такъв.

  1. executingSomething() е синхронен метод с несвързана функционалност, освен че искате цялото нещо да се провали, ако хвърля.
  2. anotherSubscription не е Subscribtion, а Observable (това са различни неща).

Ето как бих решил проблема ви:

class SomeClass {
  private method1(): Observable<any> {
    try {
      executingSomething();
    } catch (err) {
      // This would return an errorred observable, which is great, since
      // you can still subscribe to it (no need to change the return type
      // of this method).
      return throwError(err);
    }

    return this.anotherSubscription.pipe(
      tap(() => {
        console.log('Controls is reaching here!');
      }),
    );
  }

  private method2(): Observable<any> {
    // This can be whatever obervable, using of(null) for demo purposes.
    return of(null);
  }

  private parentMethod() {
    this.method1()
      .pipe(
        switchMap(valueFromAnotherSubscription => {
          // UPD1 - Implement your custom check here. This check will determine
          // whether `method2` will be called.
          if (valueFromAnotherSubscription === targetValue) {
            return this.method2();
          } else {
            // UPD1 - If the check evaluates to `false`, reemit the same value
            // using the `of` operator.
            return of(valueFromAnotherSubscription);
          }
        }),
      )
      .subscribe(() => {
        console.log('Done!');
      });
  }
}

Ключовият оператор тук, както правилно е посочил Кевин, е switchMap. Всеки път, когато наблюдаем (anotherSubscription) излъчва, switchMap ще го отмени и ще се абонира за различен наблюдаем (каквото и да е върнато от method2).

Това се случва последователно, така че method2 ще бъде абониран само за след method1 излъчвания. Ако method1 хвърля, целият тръбопровод ще се провали. Можете също така да filter резултатите от method1, за да решите дали искате или не да преминете към method2.

Освен това конструирането на наблюдаеми с помощта на new Observable вероятно не е необходимо в повечето случаи. Имам доста голямо RxJS приложение и никога не съм го използвал досега.

Актуализация 1

Вижте кода, обозначен с UPD1 коментари.

Имайте предвид, че ако anotherSubscription генерира грешка, функцията switchMap няма да бъде извикана така или иначе.

person Avius    schedule 23.12.2020
comment
Някаква конкретна причина да сте използвали tap вместо map? - person Suyash Gupta; 23.12.2020
comment
Да, целта на map е да връща различна стойност за всяка излъчена стойност. Обратно, tap се използва за странични ефекти. Смятам, че изразът log е страничен ефект, така че tap е по-подходящ. Но можете да направите това и в оператор map - ще работи, само не забравяйте да върнете стойността си: ] - person Avius; 23.12.2020
comment
Още нещо. Как да филтрирам отговора си от method1? Всъщност, как да върна нещо (като например булево, за да укажа дали да продължа по-нататък или не), когато връщам Observable<any>? Опитах да върна of(false), но не мога да използвам това false, докато не се абонирам за него и ще трябва да го направя в switchMap(res => { if (res) { } ), в противен случай ще изпълни method2. - person Suyash Gupta; 23.12.2020
comment
Добре, това, което ми е ясно е, че всеки път, когато method1 излъчва правилната стойност, вие искате абонатът да получи стойност от method2. Това, което не е ясно, е какво трябва да се случи, когато method1 излъчва лоша стойност. Виждам два възможни сценария, кажете ми кой ви трябва: A) когато method1 излъчва лоша стойност, искате напълно да игнорирате такива стойности, така че абонатът изобщо да не бъде предупреден; B) когато method1 излъчва лоша стойност, вие просто искате абонатът да получи тази стойност, като същевременно пропускате method2. Надявам се разликата да е ясна. Актуализирах отговора, използвайки опция B. - person Avius; 23.12.2020
comment
А, гледах някакъв грешен изход от конзолата и се обърках. Благодаря ти за помощта. Това работи. - person Suyash Gupta; 23.12.2020

Можете изрично да върнете стойността на serviceN на serviceN+1. Ето идеята:

private setupStuff() {
  this.initRouteParams()
    .pipe(
      switchMap(serviceId => {
        return zip(of(serviceId), this.getFileInfo(serviceId))
      }),
      switchMap(([serviceId, filename]) => {
        return zip(of(serviceId), of(filename), this.getExistingFile(serviceId, filename))
      })
    )
    .subscribe(([serviceId, filename, response]) => {
      console.log(serviceId, filename, response);
    })
}
person Kevin Zhang    schedule 23.12.2020
comment
Това, което питам и това, което предлагате, са напълно различни. Искам да върна някакъв отговор от наблюдаема, която е вложена в друга наблюдаема. И на всичкото отгоре вътрешното наблюдение дори не се изпълнява. - person Suyash Gupta; 23.12.2020
comment
Просто напишете един пример, това удовлетворява ли вашето изискване? stackblitz.com/edit/angular-ivy- rn5kfe?file=src/app/ - person Kevin Zhang; 23.12.2020

Не искам метод 2 да се изпълнява, докато или освен ако отговорът от метод 1 не е положителен

Ако просто искате излъчването от obs2 да се задейства след излъчването на obs1 (без значение какво излъчва obs1), просто използвайте switchMap:

fromEvent(document, 'click')
  .pipe(
    switchMap(() => of([1]))
  )
  .subscribe(console.log); <--- will emit [1] only after the user clicks.

Ако имате нужда obs2 да излъчва само след като obs1 излъчва конкретна стойност, използвайте switchMap с филтърен оператор (https://www.learnrxjs.io/learn-rxjs/operators/filtering/filter):

const obs$ = of(true); 
const obs2$ = of([1,2,3]);

    obs$.pipe(
        filter((result) => result === true),
        switchMap(() => obs2$)
      ).subscribe(console.log) <--- returns [1,2,3] since filter condition returns true

ако имате нужда от нещо като израз If Else, използвайте условния оператор IIF (https://www.learnrxjs.io/learn-rxjs/operators/conditional/iif):

const even$ = of('even');
const odd$ = of('odd');

interval(1000).pipe(
  mergeMap(v =>
    iif(
      () => v % 2 === 0,
      even$,
      odd$
    ))
).subscribe(console.log); <--- will emit every second a string "even" or "odd" based on the condition.
person GBra 4.669    schedule 23.12.2020