본문으로 바로가기

1) 기초적인 필터링

 

filter

of(1, 2, 3, 4)
  .pipe(filter((v) => v > 2))
  .subscribe(console.log);

// 3, 4만 출력

 

first, last

first는 take(1)과 사실상 기능이 같습니다.

of(1, 2, 3, 4).pipe(first()).subscribe(console.log); // 1만 출력
of(1, 2, 3, 4).pipe(last()).subscribe(console.log); // 4만 출력

 

2) distinct, distinctUntilChanged, distinctUntilKeyChanged

 

distinctUntilChanged가 매우 자주 사용되는 편입니다.

의미 그대로, 값이 동일하면 무시하고, 값이 달라야만 처리합니다.

comparator가 true면 ommit(생략)되고 false distinct되어서 값을 내보냅니다.

 

distinctUntilChanged<T, K>(

  comparator?: (previous: K, current: K) => boolean,

  keySelector: (value: T) => K = identity as (value: T) => K): MonoTypeOperatorFunction<T>

 

const numbers$ = of(1, 1, 1, 2, 2, 3, 3, 4, 5, 5, 5, 6, 7);

numbers$.pipe(distinctUntilChanged()).subscribe(console.log); // 1, 2, 3, 4, 5, 6, 7

 

직접 comparator를 만들수도 있다.

const userState$ = from([
  { name: "darren", loggedIn: false, token: null },
  { name: "darren", loggedIn: false, token: "asdf" },
  { name: "darren", loggedIn: true, token: "ghk" },
]);

userState$
  .pipe(
    distinctUntilChanged((prev, curr) => {
      return prev.name === curr.name; // true면 생략됨. 반면 false면 distict되지 않음.
    }),
    map((v) => v.name) 
  )
  .subscribe(console.log); // 단 1번 darren이 출력됨.

 

3) Time 친구들 : audit(Time), debounce(Time), sample(Time), throttle(Time)

이 녀석들은 time이 붙은 녀석들과 안 붙은 녀석이 비슷한 기능을 합니다.

차이점은, Time이 붙으면 정적인 값으로 해당 오퍼레이터가 실행되며 Time이 붙지 않으면 직접 동적으로 값을 제어할 수 있다는 차이가 있습니다.

 

간략하게 설명해보겠습니다. 자세한 설명은 다음 포스트에서 읽어주세요 설명 => darrengwon.tistory.com/1312

 

언제 시작하는가?

이벤트가 발생 시점부터 시간을 계산하기 시작함.

 

  • debounce : 이벤트가 발생한 시점으로부터 n 시간이 지난 후 이벤트가 발생하지 않으면 마지막 이벤트 실행. 만약 도중에 이벤트가 발생했다면 n 시간을 연장함. ("연장"에 초점을 둡시다)
  • throttle : 이벤트가 발생한 시점으로부터 n시간 이내의 이벤트 중 최초의 이벤트 실행함. 실행할 이벤트는 leading, trailing으로 조절할 수 있음. leading, tailing을 조절하여 audit을 대체할 수 있습니다.
  • sample : 이벤트가 발생한 시점으로부터 n시간을 재기 시작합니다. n시간 내에 발생한 이벤트 중 가장 최근에(나중에) 발생한 이벤트를 방출합니다. 
  • audit : 이벤트가 발생한 이후 n시간 동안 발생한 이벤트 중 가장 최근에 발생한 이벤트를 발생시킵니다. 한 번 이벤트가 발생한 후에는 다시 다른 이벤트가 발생하기 까지는 n 시간이 측정되지 않습니다. throttle의 설정을 { leading: false, trailing: true }로 놓은 것과 동일합니다.

 

https://stackoverflow.com/questions/39184789/difference-between-audit-and-debounce-in-rxjs

 

debounceTime

1초 내에 클릭 이벤트가 발생하면 방출하지 않고 지연. 1초간 아무 동작 없어야 비로소 출력함.

const click$ = fromEvent(document, "click");
click$.pipe(debounceTime(1000)).subscribe({ next: (v) => console.log(v) });

 

debounce가 사용되는 가장 대표적인 케이스인 검색창 keyup 이벤트 감지에 사용해보았습니다. 디바운싱 타임을 1초 줬습니다.

쪼~끔 신경써서 distinct까지 줬습니다.

const inputDom = document.querySelector(".input-dom");
const click$ = fromEvent(inputDom, "keyup");
click$
  .pipe(
    debounceTime(1000),
    map((e) => e.target.value),
    distinctUntilChanged()
  )
  .subscribe({ next: (v) => console.log(v) });

 

* 그냥 debounce도 있던데요?

여러분이 debounce 시간을 동적으로 제어하고 싶다면 debounce를 사용하면 됩니다.

아래 같은 경우를 보면 interval을 동적으로 제어하여서 debounce 타임을 동적으로 제어하고 있습니다.

const clicks = fromEvent(document, 'click');
const result = clicks.pipe(
  scan((i) => ++i, 1),
  debounce((i) => interval(200 * i))
);
result.subscribe(x => console.log(x));

 

 

throttleTime

 

일정한 주기 동안 발생한 이벤트 중 최초 이벤트만 실행하는 throttle입니다.

최초 이벤트인 이유는 기본값이  { leading: true, trailing: false } 이기 때문입니다.

아래 코드는 1초간 발생한 클릭 이벤트 중 최초 이벤트만을 출력합니다.

const click$ = fromEvent(document, "click");
click$.pipe(throttleTime(1000)).subscribe({ next: (v) => console.log(v) });

 

lodash와 마찬가지로 leading, trailing 옵션이 있습니다.

기본값은 { leading: true, trailing: false } 입니다. 이 의미는 최초값은 출력하겠다는 겁니다.

이와 반대로, throttle 타임 이내 발생한 이벤트 중 마지막으로 발생한 이벤트를 확인해보고 싶다면 다음과 같이 합니다.

const click$ = fromEvent(document, "click");
click$
  .pipe(throttleTime(1000, asyncScheduler, { leading: false, trailing: true }))
  .subscribe({ next: (v) => console.log(v) });

 

둘 다 false면 출력이 안되고, 둘 다 true면 둘 다 출력됩니다.

 

 

sampleTime

주어진 시간 내에 발생한 이벤트 중 가장 최근에(나중에) 발생한 이벤트를 방출합니다.

const click$ = fromEvent(document, "click");
click$
  .pipe(sampleTime(2000))
  .subscribe({ next: (e) => console.log(e.clientX) });

 

다른 녀석들과 마찬가지로 Time을 떼고 설정할 수도 있다.

아래 코드는 interval의 값 중 click할 때마다 최신의 값을 가져온다.

 

const click$ = fromEvent(document, "click");
const interval$ = interval(1000);
interval$.pipe(sample(click$)).subscribe({ next: (v) => console.log(v) });

 

auditTime

이벤트가 최초로 발생한 이후 주어진 시간 동안 발생한 이벤트 중 가장 최근에 발생한 이벤트를 발생시킵니다.

말로 하니까 이해가 안되시죠?

 

아래 마블 다이어그램을 참고해봅시다. 

a라는 이벤트가 발생했습니다. 여기서 50 만큼의 시간 동안 이벤트는, a, x, y가 발생했습니다.

주어진 시간 내에서 가장 최근에 발생한 이벤트인 y의 값을 출력합니다.

 

한 사이클이 돌고 b라는 이벤트가 발생했습니다. 여기서 또 50 만큼의 시간동안 발생한 이벤트는 b, x입니다.

주어진 시간 내에게서 가장 최근에 발생한 이벤트는 x이므로 x의 값을 출력합니다.

 

 

 

4) skip과 take

 

take: 최초의 n개만 take하겠다는 겁니다. 여기서 조선이 이것저것 붙는거구요. (take, takeLast, takeUntil, takeWhile)

skip: 생략하겠다는 거죠  (skip, skipLast, skipUntil, skipWhile)

 

take의 경우, interval과 같이 무한으로 값을 보내는 경우를 제한하기 위해 자주 사용됩니다.

또한, 무한으로 돌아가 메모리 누수를 막기 위해서 사용되기도 합니다. takeWhile같은거 제한으로 걸어두면 complete되니까...

둘이 원리가 같으므로 take만 코드를 작성하겠습니다.

 

 

 

// ====== take ======
interval(1000)
  .pipe(
    take(5),
    reduce((acc, cur) => acc + cur, 0)
  )
  .subscribe(console.log); // 10을 출력함 0, 1, 2, 3, 4가 1초 간격으로 출력되고 각 값이 더해짐

// ====== takeLast ======
of(1, 2, 3, 4)
  .pipe(
    takeLast(2),
    reduce((acc, cur, i) => acc + cur, 0)
  )
  .subscribe(console.log); // 마지막 2개만 더함. 3 + 4 라서 7 출력

//  ====== takeUntil ======
const counter$ = interval(1000);
const click$ = fromEvent(document, "click");

counter$
  .pipe(takeUntil(click$))
  .subscribe({
    next: (v) => console.log(v),
    complete: () => console.log("done"),
  }); // 즉, click 이벤트에 들어올 때까지만 interval이 돕니다.


// ====== takeWhile ======
of(1, 2, 3, 4)
  .pipe(
    takeWhile((v) => v < 3),
    reduce((acc, cur, i) => acc + cur, 0)
  )
  .subscribe(console.log); // 3이기 전까지의 값을 처함. 1 + 2라 3 출력

 

 

takeWhile<T>(predicate: (value: T, index: number) => boolean, inclusive: boolean = false): MonoTypeOperatorFunction<T>

inclusive를 true로 두면, 제한한 값에 걸리게 되는 마지막 값까지 연산에 포함합니다.

 

 

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체