避免在RXJS中嵌套订阅

时间:2020-02-16 11:46:47

标签: javascript angular typescript rxjs

我有以下代码:

someService.subscribeToChanges().subscribe(value => {
    const newValue = someArray.find(val => val.id === value.id)
    if (newValue) {
       if (value.status === 'someStatus') { 
          someArray.findIndex((a) => a.id === value.id)
          someArray.splice(index, 1);
       } else { 
          newValue.status = value.status;
       }
    } else { 
        if (value.someProp === 'abc') {
          someService.doSomeAsyncStuff().subscribe(data => {
            someService.handleData(data);
          });
        }
    }
 });

我想保持相同的逻辑,同时避免第二次订阅。

这是我尝试过的代码

sub$ = someService.subscribeToChanges().pipe(switchMap(value => {
  const newValue = someArray.find(val => val.id === value.id)
  if (newValue) {  
       if (value.status === 'someStatus') { 
          someArray.findIndex((a) => a.id === value.id)
          someArray.splice(index, 1);
       } else { 
          newValue.status = value.status;
       }
  } else { 
    return iif(() => value.someProp === 'ABC', someService.doSomeAsyncStuff);
  }
}));

sub$.subscribe(data => someService.handleData(data))

如果iif条件为true,则此方法有效,但如果为false,它将停止整个流,而我希望它继续流式传输。

3 个答案:

答案 0 :(得分:0)

您可以将它们分为三个条件

sub$ = someService.subscribeToChanges().pipe(switchMap(value => {
 const newValue = someArray.find(val => val.id === value.id)
 if (newValue) {
   if (value.status === 'someStatus') { 
      someArray.findIndex((a) => a.id === value.id)
      someArray.splice(index, 1);
   } else { 
      newValue.status = value.status;
   }
 return of(value); 
 }

    if(value.someProp === 'ABC')
      return someService.doSomeStuff()

    return of(value)
  })
)

答案 1 :(得分:0)

嵌套订阅是rxjs中的antipattern。因此,您尝试避免它的好处。我建议您仍然使用两个订阅,但不要嵌套。因为您有不同的副作用(someService.handleData和doSomeStuff)

出于重用原因,使用someArray id比较器创建您的subscriptionTochanges ID

const doSomeStuff$ = subscribeToChangesIdEqualsSomeArrayId$.pipe(
  filter(newValue => newValue)
);

创建可观察的doSomeStuff

const handleData$ = combineLatest([
  subscribeToChangesIdEqualsSomeArrayId$,
  someService.subscribeToChanges()
]).pipe(
  filter(([newValue, value]) => !newValue && value.someProp === 'abc'),
  switchMap(() => someService.doSomeStuff())
);

创建可观察的数据

doSomeStuff$.subscribe(() => doSomeStuff());
handleData$.subscribe(data => someService.handleData(data));

最终用法

{{1}}

仅供参考:我使用了您在问题中使用的所有外部和内部变量名称(从管道角度来看)。我可能会为了阅读而调整它们:)

答案 2 :(得分:-1)

将所有逻辑保持在一个链中总是总好得多,因此您可以像这样进行操作。由于您不需要newValue,因此非常简单:

someService.subscribeToChanges()
  .pipe(
    switchMap(value => {
      const newValue = someArray.find(val => val.id === value.id);
      if (!newValue && value.someProp === 'abc') {
        return someService.doSomeStuff();
      }
      return of(undefined);
    }),
  )
  .subscribe(data => {
    if (data === undefined) { // When data is set means we ran the second Observable
      doSomeStuff();
    } else {
      someService.handleData(data);
    }
  });

如果对于someService.doSomeStuff()undefined的有效响应,那么我将其响应包裹在诸如{ response: data }之类的对象上,以便您始终知道它的来源。

请尽量避免进行多次订阅,直到真正必要时为止,因为这样做会带来更多您必须了解的事情(例如订阅顺序,共享Observable等等),并且始终容易以自上而下的顺序读取RxJS链,例如这是同步代码。