Typescript 为什么在Rxjs中需要Tap运算符

Typescript 为什么在Rxjs中需要Tap运算符,typescript,rxjs,Typescript,Rxjs,我是Rxjs新手,正在观看David Acosta关于Rxjs运算符的教程之一。 在这篇文章中,他说当我们不需要接触可观测数据时,我们可以使用tap操作符。 因此,tap函数中的数据不会更改 我写了下面的代码 const source = Observable.of("david"); source.pipe( tap(x => x.toString().toUpperCase()) ).subscribe(x => console.log(x));

我是Rxjs新手,正在观看David Acosta关于Rxjs运算符的教程之一。 在这篇文章中,他说当我们不需要接触可观测数据时,我们可以使用tap操作符。 因此,tap函数中的数据不会更改

我写了下面的代码

 const source = Observable.of("david");

    source.pipe(
      tap(x => x.toString().toUpperCase())
    ).subscribe(x => console.log(x));
我得到的输出是
david
。 但我想知道

source.subscribe(x => console.log(x));
给出相同的输出
david
。那我们为什么需要水龙头接线员呢。 这对我来说有点神秘,我无法找到合适的资源来解释
TAP
操作符


有谁能向我详细解释一下这一点,并向我推荐一些Rxjs操作符的好教程或文档吗

1-存储/查看可观察流的值:

const source = Observable.of("david");

let name;

source.pipe(
   tap(x => name = x)
).subscribe(x => console.log(x));

console.log('variable name: ', name);
2-在管道上的某个位置(而不是在订阅上)执行一些副作用

注意:在代码中使用tap运算符执行副作用时要小心。有些是合理的和正确的,但其他的可以通过对.subscribe回调执行您的副作用来解决


希望这有帮助

Tap运算符通常用于两种使用情形:

1-存储/查看可观察流的值:

const source = Observable.of("david");

let name;

source.pipe(
   tap(x => name = x)
).subscribe(x => console.log(x));

console.log('variable name: ', name);
2-在管道上的某个位置(而不是在订阅上)执行一些副作用

注意:在代码中使用tap运算符执行副作用时要小心。有些是合理的和正确的,但其他的可以通过对.subscribe回调执行您的副作用来解决


希望这有帮助

例如,当您想做某事时,您可以使用
轻触
,但您不想获取所有值​​ 因此(您只需要一个特定的类型),因此您可以过滤您的流。 例如,您构建的arryay中的数字只能被2整除,您不需要其他数字

但是,使用
tap
在很多时候都是一个干净的代码

简单代码参考:

myObservable$ = new Observable(observer => {
    observer.next(1);
    observer.next(2);
    observer.next(3);
    observer.next(4);
    observer.next(5);
    observer.next(6);
    observer.complete();
});


this.myObservable$
  .pipe(
    tap(() => console.log('got Value!')),
    filter(value => (value % 2) === 0),
  )
  .subscribe(value => {
    console.log('filtered: ', value);
    this.mySpecificArray.push(value);
   });

例如,当您想要执行某项操作时,可以使用
轻触
,但您不想获取所有值​​ 因此(您只需要一个特定的类型),因此您可以过滤您的流。 例如,您构建的arryay中的数字只能被2整除,您不需要其他数字

但是,使用
tap
在很多时候都是一个干净的代码

简单代码参考:

myObservable$ = new Observable(observer => {
    observer.next(1);
    observer.next(2);
    observer.next(3);
    observer.next(4);
    observer.next(5);
    observer.next(6);
    observer.complete();
});


this.myObservable$
  .pipe(
    tap(() => console.log('got Value!')),
    filter(value => (value % 2) === 0),
  )
  .subscribe(value => {
    console.log('filtered: ', value);
    this.mySpecificArray.push(value);
   });

点击与添加订阅并不完全相同。例如,如果您有一个HTTP服务器的客户端,那么您可能会有一些类似于

function getItems(): Observable<Item[]> {
  return makeSomeExpensiveHttpCall()
    .map(rsp => doSomeExpensiveParsing(rsp))
    .tap(items => console.log(`Received ${items.length} items`));    
}
函数getItems():可观察{
返回makeSomeExpensiveHttpCall()
.map(rsp=>doSomeExpensiveParsing(rsp))
.tap(items=>console.log(`Received${items.length}items`);
}
…乍一看相当于

function getItems(): Observable<Item[]> {
  const result = return makeSomeExpensiveHttpCall()
    .map(rsp => doSomeExpensiveParsing(rsp));
  result.subscribe(items => {
    console.log(`Received ${items.length} items`);
  });
  return result;
}
函数getItems():可观察{
const result=返回makeSomeExpensiveHttpCall()
.map(rsp=>doSomeExpensiveParsing(rsp));
结果。订阅(项目=>{
log(`Received${items.length}items`);
});
返回结果;
}

但是,按照当前编写的方式,每次调用
subscribe
时,它都将执行
makeSomeExpensiveHttpCall
doSomeExpensiveParsing
。因此,在第二个示例中,您可能会调用这些函数两次,而不是一次。您将向服务器发送两个相同的HTTP请求。我们当然希望避免这种情况。

点击与添加订阅并不完全相同。例如,如果您有一个HTTP服务器的客户端,那么您可能会有一些类似于

function getItems(): Observable<Item[]> {
  return makeSomeExpensiveHttpCall()
    .map(rsp => doSomeExpensiveParsing(rsp))
    .tap(items => console.log(`Received ${items.length} items`));    
}
函数getItems():可观察{
返回makeSomeExpensiveHttpCall()
.map(rsp=>doSomeExpensiveParsing(rsp))
.tap(items=>console.log(`Received${items.length}items`);
}
…乍一看相当于

function getItems(): Observable<Item[]> {
  const result = return makeSomeExpensiveHttpCall()
    .map(rsp => doSomeExpensiveParsing(rsp));
  result.subscribe(items => {
    console.log(`Received ${items.length} items`);
  });
  return result;
}
函数getItems():可观察{
const result=返回makeSomeExpensiveHttpCall()
.map(rsp=>doSomeExpensiveParsing(rsp));
结果。订阅(项目=>{
log(`Received${items.length}items`);
});
返回结果;
}

但是,按照当前编写的方式,每次调用
subscribe
时,它都将执行
makeSomeExpensiveHttpCall
doSomeExpensiveParsing
。因此,在第二个示例中,您可能会调用这些函数两次,而不是一次。您将向服务器发送两个相同的HTTP请求。我们当然希望避免这种情况。

并非特定于Rxjs,但通常使用tap作为调试工具,在更改序列的这一点上检查数据。您可能能够对数据进行变异并继续,但这通常不是一个好主意。管道/流的不同实现可能有不同的原理,或者实际上可能完全不允许它,或者允许它没有问题。一般来说,你不想在点击时进行转换,因为它可能会扰乱其他一些操作。那么点击的副作用是什么呢。正如您所说的,
不想进行转换
。这取决于实现。例如,在Java示例中,编译器可能会优化流上的
.tap
操作。如果依赖它,那么代码可能会随机失效。在其他地方,这可能会打乱不同的优化。或者,这可能会扰乱操作的并行化。最一般的答案是,对数据的操作应该通过以下方式完成-这简化了优化操作的数学过程,并允许了琐碎的多线程,因为您需要更改状态。例如,如果您希望将数据保存在本地存储中,但不想在可观察的管道中转换数据,则可以使用
tap(x=>localStorage。