rxjs 只在第一次执行 tap

hua*_*tao 13 javascript rxjs

我只想在获得第一个发出的值时执行 tap()

就像是:

Observable
  .pipe(
     tap(() => { /* execute only when I get the first emitted value */ })
  )
  .subscribe(() => {
     // .....
  })
Run Code Online (Sandbox Code Playgroud)

jal*_*jal 17

您可以在像concatMap. 与其他方法不同,这对于所选索引是完全灵活的。假设您想要点击第二次发射index === 1或任何谓词,例如index % 2 === 0

// these are because of using rxjs from CDN in code snippet, ignore them
const {of, interval} = rxjs;
const {take, tap, concatMap} = rxjs.operators;


// main code
const stream = interval(250).pipe(take(4))

stream.pipe(
  concatMap((value, index) => index === 0
    ? of(value).pipe(
        tap(() => console.log('tap'))
      )
    : of(value)
  )
)
.subscribe(x => console.log(x));
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/@reactivex/rxjs@6.x/dist/global/rxjs.umd.js"></script>
Run Code Online (Sandbox Code Playgroud)


Gog*_*eli 12

如果我正确理解您的想法,您只想tap()在流订阅开始时执行,而不是其他时间。这是我的自定义运算符:

import { Observable, of } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';

export function startWithTap<T>(callback: () => void) {
  return (source: Observable<T>) =>
    of({}).pipe(tap(callback), switchMap((o) => source));
}
Run Code Online (Sandbox Code Playgroud)

例如,此运算符的用法是:

this.api.getData().pipe(
  startWithTap(() => this.loading.start()),
)
Run Code Online (Sandbox Code Playgroud)

这是我的实际代码示例,当有人订阅由 api 服务(通过 httpClient)创建的 Observable 时,加载开始。


更新

使用 this 而不是上面的实现,因为这个只使用defer, 而不是使用of, tapand switchMap

export function startWithTap<T>(callback: () => void) {
  return (source: Observable<T>) =>
    defer(() => {
      callback();
      return source;
    });
}
Run Code Online (Sandbox Code Playgroud)

  • @enno.void 更新的解决方案的工作方式相同,仅使用 1 个 rxjs 运算符而不是 3 个,因此复杂性更少 (2认同)

jBu*_*olz 8

我喜欢jal 的回答方法,并建议将其包装在自己的运算符中:

export function tapOnce<T>(tapFn: (t: T) => void, tapIndex = 0): OperatorFunction<T, T> {
  return source$ => source$.pipe(concatMap((value, index) => {
    if (index === tapIndex) {
      tapFn(value);
    }
    return of(value);
  }));
}
Run Code Online (Sandbox Code Playgroud)

用法如下:

stream.pipe(tapOnce(() => console.log('tapping once'), 1));
Run Code Online (Sandbox Code Playgroud)

这甚至可以进一步抽象为一个运算符,该运算符采用一个函数来确定是否应该根据给定的值/索引进行点击:

export function tapWhen<T>(tapFn: (t: T) => void, evaluateFn: (index: number, t: T) => boolean): OperatorFunction<T, T> {
  return source$ => source$.pipe(concatMap((value, index) => {
    if (evaluateFn(index, value)) {
      tapFn(value);
    }
    return of(value);
  }));
}
Run Code Online (Sandbox Code Playgroud)