【问题标题】:Avoid nested subscription in RXJS避免 RXJS 中的嵌套订阅
【发布时间】:2020-05-31 12:57:15
【问题描述】:

我有以下代码:

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 条件为真,则此方法有效,但如果为假,它只会停止整个流,而我希望它继续流。

【问题讨论】:

  • 应该是return doSomeStuff();
  • @Andrew 不,不必。这些只是边做的一些操作。
  • 问题不清楚且缺少信息。如果 doSomeStuff 返回 void 则如果 newValue 为真,则会出错
  • @AndrewAllen 我更改了代码以显示 doSomeStuff 的作用。

标签: javascript angular typescript rxjs


【解决方案1】:

你可以把它们分成三个条件

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)
  })
)

【讨论】:

  • 在 switchmap/管道中产生副作用通常不是一个好习惯。在小型项目中它肯定会起作用,但你的项目越大,它的缺点就越多。
  • 不确定这里 doSomeStuff 的意图以及是否是副作用,需要进一步澄清。
  • 是的,doSomeStuff() 不是异步的。它只是修改了一些数组。
  • 我用 of() 包装了它,所以它会执行并为 switchMap 提供一个 observable,并取决于你希望这个同步函数返回什么,你可以做什么 of(dosomestuff()).pipe(mapTo (...))
  • @FanCheung,但 doSomeStuff 不是异步的。我用 doSomeStuff 中的内容更新了我的代码。
【解决方案2】:

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

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

const subscribeToChangesIdEqualsSomeArrayId$ = someService.subscribeToChanges().pipe(
  map(value => someArray.find(val => val.id === value.id)
);

创建你的 doSomeStuff Observable

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

创建您的 handleData Observable

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));

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

【讨论】:

  • 在不将 someArray 更改为 observable 的情况下是否可以做同样的事情?因为这需要对应用进行大量更改。
  • 是的,那当然要容易得多。答案已更新。现在您不需要将数组作为 observable 给出。您可以直接在 subscribeToChangesIdEqualsSomeArrayId$ observable 中使用它作为副作用。一切都属于你。
【解决方案3】:

将所有逻辑保持在一条链中总是更好,因此您可以像这样进行操作。由于您不需要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 } 之类的对象包装它的响应,这样您就永远知道它来自哪里。

在真正有必要之前尽量避免进行多次订阅,因为这会带来更多你必须注意的事情(比如订阅顺序、共享 Observables 等等),而且以自上而下的顺序阅读 RxJS 链总是更容易,例如这是同步代码。

【讨论】:

  • 问题是我需要doSomeStuff 的数据我已经更新了我的代码以显示我正在使用这些数据做什么。你能查一下吗?
  • 我看不到你在你的例子中做了什么
  • 我将 doSomeStuff 替换为等效代码,在第一个 if 内,您可以看到它正在使用例如 newValue.status = value.status;,而 subscribe 在您的代码中不可用。跨度>
  • 我太糊涂了,说实话
猜你喜欢
  • 2020-08-18
  • 2019-10-03
  • 2023-01-11
  • 2023-01-30
  • 2022-06-22
  • 2018-11-27
  • 2022-08-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多