tap 操作符在 RxJs 中有什么有用的功能?

huangapple go评论66阅读模式
英文:

What useful function does tap operator bring in RxJs?

问题

tap操作符实际上是做什么的?当您在一个值上执行它时会发生什么(应该发生什么)?它是否只是执行一些代码?如果它只是执行一些代码,那么它是否只是该代码的包装器?

例如,对于下面的代码,它实际上做了什么?

clickStream$.pipe(
   tap((event) => {
       event.stopPropagation();
       event.preventDefault();
   }),
   debounce(300),
   map((event) => event.key)
).subscribe((key) => console.log('key debounced: ', key))

或者在这个示例中:

export class LoggingInterceptorService implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler) {
    console.log('Outgoing request:');
    console.log(req.url);
    return next.handle(req).pipe(tap(event => {
      if (event.type === HttpEventType.Response) {
        console.log('Incoming response: ');
        console.log(event.body);
      }
    }));
  }
}
英文:

What does tap operator actually do? What happenes (should happen) when you execute it on a value? Should it just execute some code? If it should just execute some code, then is it just a wrapper for that code?

For example, for does it do below?

clickStream$.pipe(
   tap((event) =&gt; {
       event.stopPropagation();
       event.preventDefault();
   }),
   debounce(300),
   map((event) =&gt; event.key)
).subscribe((key) =&gt; console.log(&#39;key debounced: &#39;, key))

or in this example

export class LoggingInterceptorService implements HttpInterceptor {
  intercept(req: HttpRequest&lt;any&gt;, next: HttpHandler) {
    console.log(&#39;Outgoing request:&#39;);
    console.log(req.url);
    return next.handle(req).pipe(tap(event =&gt; {
      if (event.type === HttpEventType.Response) {
        console.log(&#39;Incoming response: &#39;);
        console.log(event.body);
      }
    }));
  }
}

答案1

得分: 2

通过将Rxjs操作符串在一起,我们从一个observable开始,最终得到另一个observable。

const userBooks$ = getUser(emailAddress).pipe(
                     switchMap(user => getBooksForUser(user.id))); 
  1. 我们从一个返回Observable<User>的方法开始。
  2. 我们将其传递给switchMap,并调用另一个返回Observable<Book[]>的方法。

这就是Rxjs的函数式响应性特性。您可以将操作符串在一起以转换流。

您可以将这些操作符看作是列表推导方法,如map、reduce、filter、merge...但用于异步流,而不是数组或列表。

因此,如果我们只能将Observable<T1>管道到操作符并获得Observable<T2> -- 那么我们如何执行其他操作,比如调用console.log(..) -- 或向用户显示错误?或者执行与Rxjs操作符链外部世界的交互所需的任何操作?任何涉及状态、调用外部内容的操作都是副作用。

const userBooks$ = getUser(emailAddress).pipe(
                     tap(user => console.log('user id: ', user.id)),
                     switchMap(user => getBooksForUser(user.id).pipe(
                       tap(books => console.log('books: ', JSON.stringify(books)))));

tap(..) 不会改变流的转换 -- 它完全是非破坏性的。因此,通常用于调试,可以安全地添加和移除它们,而不会影响操作符链。

英文:

By piping Rxjs operators together we start out with one observable, and end with another observable.

const userBooks$ = getUser(emailAddress).pipe(
                     switchMap(user =&gt; getBooksForUser(user.id)); 
  1. We start out with a method that returns an Observable&lt;User&gt;
  2. We pipe this to switchMap and call another method that returns and Observable&lt;Book[]&gt;

This is the functional reactive nature of Rxjs. You pipe together operators to transform streams.

You can think of these as list comprehension methods like map, reduce, filter, merge ... but for asynchronous streams -- not for arrays or lists.

So if all we can do with pipe an Observalbe&lt;T1&gt; to operators and get Observable&lt;T2&gt; out -- how do we do other things, like call console.log(..) -- or show an error to the user? Or whatever we need to do that interacts with the world outside the chain of Rxjs operators. Anything that touches state, calls something, outside of the operators is a side effect.

const userBooks$ = getUser(emailAddress).pipe(
                     tap(user =&gt; console.log(&#39;user id: &#39;, user.id),
                     switchMap(user =&gt; getBooksForUser(user.id).pipe(
                       tap(books =&gt; console.log(&#39;books: &#39;, JSON.stringify(books))); 

tap(..) does not change the transformation of the stream -- it is completely non destructive. For this reason it is often used for debugging, as these can be safely added and removed without affecting the chain of operators.

答案2

得分: 0

这是关于将副作用与纯(更加精确地说是数学上的)计算分开的问题。受到函数式编程的启发,其中副作用要么根本不可能存在(因此这种语言对于许多现实世界的情景完全无用:例如,您永远无法查询数据库或将内容写入控制台),要么只允许在某些明确条件下存在(就像Haskell中著名的IO单子一样),rxjs尽力遵循这种思维方式。不幸的是,它仍然缺乏明显的分隔线:与上述的Haskell相反,可能会在tap之外引入副作用。例如,有人编写类似于myObservable.pipe(map(x =&gt; { console.log(x); return x * x; });的代码(你永远无法将这段代码转换为Haskell而不使用IO类型)- 这里副作用无情地与纯计算混在一起。JavaScript是一种不强制执行纯度的编程语言;但rxjs尝试提供一组有用的原语,允许您将副作用引入到纯代码之外:myObservable.pipe(tap(console.log), map(x =&gt; x * x))。现在map是纯的,具有所有纯度的好处,而tap则是受控不纯性的明确定义区域。纯度确实不是框架的核心思想,但可以说是其中之一。

P.S. 在这一点上,您可能认为"数学计算"是您在学校学到的一些内容:例如,对一些整数进行乘法运算或任何代数运算;这并不完全正确。请花些时间学习纯函数和不纯函数之间的区别,以及它们的应用和影响,以获得对这个问题的充分理解。

英文:

It's about separating side-effects from pure (mathematical to be more precise) computations. Inspired by functional programming, where side-effects are either impossible at all (and thus such a language is completely useless for a lot of real-world scenarios: for example, you can never query a database or write something to console) or allowed only within some explicit conditions (like the famous IO monad in Haskell), rxjs does its best to follow such a way of thinking. Unfortunately, it still lacks razor shape separation line: on the contrary to abovementioned Haskell one might introduce a side-effect outside the tap: let say, one codes something like myObservable.pipe(map(x =&gt; { console.log(x); return x * x; }); (you can never translate this code to Haskell bypassing IO type) - there is side-effect mercilessly mixed up with pure computation. JavaScript is a programming language does not enforce purity at all; yet rxjs tries to provide a useful set of primitives allowing you to introduce side-effects apart from pure code: myObservable.pipe(tap(console.log), map(x =&gt; x * x)). Now the map is pure with all the benefits of purity and tap is a well-defined area of controlled impurity. Purity indeed not the central idea of framework, but arguably one of them.

P.S. At this point you might think that a "mathematical computation" is something you've learned at school: multiplying some integers or whatever algebra; that's not quite correct. Don't hesitate to take some time and learn the distinction between pure and impure functions and their applications and consequences to get an adequate understanding of the problem.

答案3

得分: -1

"tap" 运算符用于引入副作用
您可以将副作用视为发生在您的主要操作之外的事情。

在您的示例中,clickStream$ 的观察者仅对在发生点击事件时接收通知感兴趣。tap 运算符内部发生的事情是一个副作用,因为除了发出点击事件之外,您还执行其他操作,比如 event.stopPropagation()event.preventDefault()

另一个副作用可能是记录。另一个可能是在用户提交表单时向服务器发送请求。

英文:

The tap operator is used to introduce side effects.
You can think of a side effect as something that happens on the side of your main action.

In your example, the clickStream$'s observers are only interested in receiving notifications whenever click events occur. What happens inside the tap operator is a side effect, because, besides emitting click events, you also have other actions like event.stopPropagation() and event.preventDefault().

Another side effect could be logging. Another one would be sending a request to the server when the user submits a form.

huangapple
  • 本文由 发表于 2020年1月3日 19:07:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/59577497.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定