发出第一个值,然后忽略计时器内发出的值

时间:2019-07-18 18:59:55

标签: javascript angular rxjs observable

这是我的代码:
app.component.ts

notifier$ = new BehaviorSubject<any>({});

notify() {
  this.notifier$.next({});
}

app.component.html

<div (scroll)="notify()"></div>
<child-component [inp]="notifier$ | async" />

问题在于,当用户滚动时,notify()函数会被重复调用,并且我只想在用户每次滚动时一次调用notify()

我可以这样完成我想要的事情:

scrolling = false;
scrollingTimer: NodeJS.Timer;

notify() {
  clearTimeout(this.scrollingTimer);
  if (!this.scrolling) {
    this.notifier$.next({});
  }
  this.scrolling = true;
  this.scrollingTimer = setTimeout(() => (this.scrolling = false), 1000);
}

但我会希望使用rxjs进行此操作。但是debounceTime与我想要的相反,throttleTimeauditTime都不是我想要的。有办法吗?

3 个答案:

答案 0 :(得分:2)

您可以像这样建立一个可观察的物体:

const scroll$ = fromEvent(document, 'scroll');

const scrollEnd$ = scroll$.pipe(
  switchMapTo(
    timer(1000) // on every scroll, restart a timer
  )
);

const scrollStart$ = scrollEnd$.pipe( // the scroll end event triggers switch to scroll$
  startWith(0), // but start it off
  switchMapTo(
    scroll$.pipe( // then just take the first scroll$ event
      first()
    )
  )
);

scrollStart$.subscribe(v => console.log('scroll start'));

您可以将其概括为一个运算符:

function firstTimeout(timeout: number) { // welcoming notes on a better name
  return input$ => {
    const inputTimeout$ = input$.pipe(
      switchMapTo(timer(timeout))
    );

    return inputTimeout$.pipe(
      startWith(0),
      switchMapTo(input$.pipe(first()))
    );
  };
}

并像这样使用它:

notifier$.pipe(firstTimeout(1000)).subscribe(v => console.log('took one'));

在这种情况下,一个好主意是将其包装在指令中以方便重用:

@Directive({
  selector: '[scrollStart]'
})
export class ScrollStartDirective {

  private scrollSource = new Subject();

  @HostListener('scroll', ['$event'])
  private onScroll(event) {
    this.scrollSource.next(event);
  }

  @Output()
  scrollStart = new EventEmitter();

  constructor() {
    this.scrollSource.pipe(firstTimeout(1000)).subscribe(this.scrollStart);
  }
}

然后您可以像这样使用它:

<div (scrollStart)="notify()"></div>

答案 1 :(得分:0)

您可以使用take(1)

this.inp.pipe(
  take(1),
).subscribe(res => console.log(res));

take(1)仅获取第一个值并完成。无需进一步的逻辑。

您当然应该在子组件中使用以上内容:)

此外,由于您的观察结果即将完成...您可以创建一个新主题,并在每次滚动时将其传递给子组件


notify() {
  this.notifier$ = new BehaviorSubject<any>({});
  this.notifier$.next({});
}

答案 2 :(得分:0)

用户滚动时,您希望notify$针对每个滚动事件发出。这提供了恒定的发射值流。因此,您希望notifier$在流开始时发出一次,在空闲1秒时再次发出。

notify$ = new Subject();

notifier$ = merge(
    notify$.pipe(first()),
    notify$.pipe(switchMap(value => of(value).pipe(delay(1000))))
).pipe( 
    take(2),
    repeat()
);

<div (scroll)="notify$.next()"></div>

您合并了两个观察值。第一个立即发射,第二个在1秒的延迟后发射。您使用switchMap以便延迟的可观察项始终重新启动。

我们采用下两个触发流完成的值,并使用repeat来重新开始。