rxjs仅在第一次执行tap

时间:2019-01-08 20:27:50

标签: javascript rxjs

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

类似的东西:

result

7 个答案:

答案 0 :(得分:3)

如果有人感兴趣,这是 tapN 非常简单实现。因此,它将为每个排放执行指定的回调函数,直到排放数量等于nEmissions。为了仅对第一个元素执行tap()函数,您可以执行 tapN(1),但是您也可以使用tapN(3)对3秒钟的发射执行tap。 >

/* Executes the specified callback function for each emission until the number of emissions is equal to nEmissions*/
export const tapN = <T>(nEmissions, callback: (T) => void) => (source$: Observable<T>): Observable<T> =>
    defer(() => {
        let counter = 0;
        return source$.pipe(tap((item) => {
            if (counter < nEmissions) {
                callback(item);
                counter++;
            }
        }));
    });

在您的代码中:

Observable
  .pipe(
     tapN(1, () => { /* this code would be only executed on the first emitted value */ })
  )
  .subscribe(() => {
     // .....
  })

答案 1 :(得分:2)

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

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);
  }));
}

用法如下:

stream.pipe(tapOnce(() => console.log('tapping once'), 1));

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

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);
  }));
}

答案 2 :(得分:1)

您可以在地图操作符中使用索引,例如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));
<script src="https://unpkg.com/@reactivex/rxjs@6.x/dist/global/rxjs.umd.js"></script>

答案 3 :(得分:0)

除了已经提到的选项之外,您还使用multicast

multicast(new Subject(), s => concat(
  s.pipe(
    take(1),
    tap(v => console.log('tap', v)),
  ),
  s
)

实时演示:https://stackblitz.com/edit/rxjs-shvuxm

答案 4 :(得分:0)

您可以像下面这样共享()您的主要Observable:

git clone git@github.com:username/repo.git
cd repo
touch some_file
git commit -am "message"
git push origin master

https://stackblitz.com/edit/typescript-qpnbkm?embed=1&file=index.ts

这是一个类似learn-rxjs

的示例

答案 5 :(得分:-1)

(更新我之前的错误答案)

基于Cartant的评论和链接,他已经完成了创建操作符的工作,该操作符位于'rxjs-etc'包中。一个基于他的操作员的解决方案是安装“ rxjs-etc”,然后:

import { initial } from 'rxjs-etc/operators';

observable$.pipe(
    initial(src$ => src$.pipe(tap(() => {/* execute only on first value */})))
).
subscribe(() => {
    // ..... 
})

有效的StackBlitz示例。

答案 6 :(得分:-1)

如果我正确理解了您的想法,则只在流订阅开始时执行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));
}

例如,使用此运算符的方式将是:

this.api.getData().pipe(
  startWithTap(() => this.loading.start()),
)
  

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