【问题标题】:do (tap) vs subscribe做(点击)与订阅
【发布时间】:2017-07-26 08:32:57
【问题描述】:

编辑:在 RxJs 6 之前,tap 被称为 do 。更新了标题以反映点击。

我想了解使用 Observables 的 .subscribe.do 方法的最佳做法是什么。

例如,如果我需要在从服务器加载初始数据后做一些工作

const init$: Observable<MyData> = this._dataService.getData();

init$
  .do((initialData: MyData) => {
    this.data = initialData; // at this step I am initializing the view
  })
  .switchMap(() => loadExtraData)
  .subscribe((extraData) => {
     doSomething(extraData, this._data); // I need this._data here
  }); 

.subscribe 我也可以这样做

const init$: Observable<MyData> = this._dataService.getData()
  .shareReplay(1);

init$
  .subscribe((initialData: MyData) => {
    this.data = initialData; // at this step I am initializing the view
  })

init$
  .combineLatest(loadExtraData)
  .subscribe(([initialData, extraData]) => {
     doSomething(extraData, initialData); // I need this._data here
  }); 

哪个更好,为什么?

【问题讨论】:

    标签: javascript angular rxjs


    【解决方案1】:

    编辑:对于 RxJS 6 或更高版本,将do 读作tap

    do 用于副作用。 subscribe 用于调用 observable。将 do 替换为 subscribe 会产生不希望的结果。用do 替换subscribe 甚至不会调用流。

    考虑这些例子:

    使用订阅

    const testObservable = Rx.Observable.create(function(observer){
       console.log('some heavy task, may be network call ');
       observer.next('success');
    });
    
    testObservable.subscribe(function(res){
        console.log('action 1');
    });
    
    testObservable.subscribe(function(res){
       console.log('action 2');
    });
    

    上面代码的输出是

    "some heavy task, may be network call "
    "action 1"
    "some heavy task, may be network call "
    "action 2"
    

    您可以看到Rx.Observable.create 被执行了两次。我们的目标是只做一次,但与行动 2 一起,也做行动 1

    使用

    const testObservable = Rx.Observable.create(function(observer){
       console.log('some heavy task, may be network call ');
       observer.next('success');
    });
    
    testObservable
        .do(function(res){
            console.log('action 1');
        })  
        .subscribe(function(res){
            console.log('action 2');
        });
    

    输出将是

    "some heavy task, may be network call "
    "action 1"
    "action 2"
    

    这正是我们真正想要的。我们需要“行动 2”,但在此之前还需要“行动 1”。

    为什么叫副作用

    因为它不会像其他运算符那样影响流的流动。它接受响应,做某事,即使它修改了响应,流也会忽略它。例如:

    testObservable
        .do(function(res){
            console.log('action 1');
            return res+'some other text';
        })  
        .subscribe(function(res){
            console.log('action 1');
        });
    

    上面的代码仍然会给出和以前一样的输出。因此,无论您在do 中执行什么操作,流都会忽略它并继续执行。

    如果我们正在做纯粹的“函数式反应式编程”,我们不希望流中有任何副作用。所以,do 不鼓励使用,主要仅用于调试目的。

    【讨论】:

    • 我看不出这个答案有什么意义。在do-example 中,只需将console.log('action 1'); 放在subscribe 回调中?如果您想避免与创建订阅相关的繁重任务,请使用 share 之类的内容多播流。
    • @blid 前任只是为了说明 tapsubscribe 之间的差异。我们可以通过多种方式实现相同的目标。这不是前任的目标。 share 是完全不同的概念恕我直言,它只是反映了一个可观察的。
    【解决方案2】:

    在你使用subscribe() 在操作符之间建立连接之前,Observable 链不会做任何事情。所以你总是必须使用subscribe() 方法。

    do() 运算符仅用于产生副作用。

    根据我的经验,通常不可能在 subscribe() 中完成所有操作,有时您需要使用 do(),因为您的应用程序逻辑需要按特定顺序执行操作。

    所以总的来说,我会尽量避免使用do(),并将我的大部分逻辑放入subscribe()。只有当我必须以不同的顺序执行操作时,我才会使用do()

    【讨论】:

    • 在你使用 subscribe() 之前,Observable 链不会做任何事情:这是不正确的:热和冷 observables 之间的区别。
    • @Hitmands 即使你有一个热的 Observable,它也可以发出它想要的任何东西,但是直到你专门调用 subscribe() 才会创建运算符之间的连接。所以在你订阅之前,即使do() 也不会收到任何东西。此外,当没有观察者接收数据时,显然 OP 不会使用热的 Observables 来获取数据。
    【解决方案3】:

    在任何情况下,您几乎都不需要在服务中使用subscribe。如果您的数据流是以一种真正反应式的方式完成的,那么您只需订阅消费者(由 Angular 中的组件表示)来运行整个流 shebang。这允许作为反应方法基础的流组合。避免将观察者的某些内容保存到本地范围内,这也违反了流纯度。您可能更愿意编写一个流来传递所有必要的数据或组合流。

    【讨论】:

    • @andré-staltz 建议仅将.do 运算符用于调试并在.subscribe 中执行所有副作用以避免隐藏副作用
    【解决方案4】:

    我在tap (do) 中做所有事情,因为它允许返回 observable,然后将它与其他附加管道连接。例如,您可以在服务中使用tap,始终返回 observable 作为结果。它使您可以根据需要更灵活地处理它,如果明天您决定在那里添加更多内容,则无需重构订阅即可点击。最后,我只在最后(渲染之前)加上.subscribe()(括号中没有)订阅。 此外,它使代码保持一致 - 所以你知道你所有的副作用总是在tap,而不是“一些正在点击的东西和订阅的东西”。

    附:如果您使用 Angular,通常可以在视图中使用 | async 管道,它会自动订阅,因此使用 .tap 而不是 .subscribe 来获得副作用是另一个原因。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-12
      • 2019-12-29
      • 1970-01-01
      • 2012-05-11
      • 2018-12-14
      • 2017-03-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多