何时在 RxJS 中使用 tap() 以及何时使用 map()

when to use tap() and when to use map() in RxJS

文档使用以下代码描述了 tap():

import { fromEvent } from 'rxjs';
import { tap, map } from 'rxjs/operators';

const clicks = fromEvent(document, 'click');
const positions = clicks.pipe(
  tap(ev => console.log(ev)),
  map(ev => ev.clientX),
);
positions.subscribe(x => console.log(x));

但是我不明白为什么要用tap。难道只是记录。解释说

Map every click to the clientX position of that click, while also logging the click event

Tap

对源 Observable 的每个发射执行副作用,但是 return 与源相同的 Observable

示例:

tap(value => console.log(value))

Map

将给定的项目函数应用于源 Observable 发出的每个值,并将结果值作为 Observable 发出。

示例:

map(value => 'prefix' + value)

为什么不使用 map 或其他运算符来实现 log

等副作用

您可以使用 map、scan 和任何其他获取函数和 return 单个结果的运算符来实现副作用。这将使您的代码将来很难 understand/debug/find 错误。前提是,一般来说在管道中使用side effects是不好的。如果您 need/want 并且不希望每个记录点都有大量订阅,则记录可能是一个例外。因此水龙头是有用的。您可以根据以下步骤对其进行调整:

  1. 尽量避免副作用
  2. 如果您需要一些(日志记录),请使用 tap 来澄清存在副作用

您可以但不应该如何做的示例

switchMap(value => {
  console.log(value); // Side effect 1
  const newValue = foo(value);
  console.log(newValue); // Side effect 2
  return of(newValue);
})

您可以和应该如何做的示例

tap(console.log), // Side effect 1
switchMap(value => of(foo(value))),
tap(console.log) // Side effect 2

最后一句话:当您最初编写的代码不会带来太多好处。您的项目越大,当其他人尝试查找错误时,它会大大节省时间。