redux-saga의 throttle과 debounce를 사용해보십시오.

소개



redux-saga의 API reference 을 바라보고 있고, 읽은 것만으로는 움직임이 이미지하기 어려웠던 throttle과 debounce에 대해서, 실제로 움직여 이미지를 잡아 보았습니다. (영어 어렵다)
실제 개발에서 사용한 것은 아니기 때문에 샘플을 만들어 움직여 보았을 뿐입니다. 샘플 소스는 여기

사용한 redux-saga 버전: 1.0.5

TL;DR



기본적으로 두 API는 take와 마찬가지로 action의 dispatch를 기다리고 작업을 시작합니다. 같은 action이 단시간 내에 복수 dispatch되었을 때의 거동이 다릅니다.
  • throttle: action이 dispatch되면 작업을 시작합니다. 지정된 시간 내에 동일한 action이 디스패치되면 작업을 시작하지 않고 최신 작업을 하나만 유지하고 지정된 시간이 지나면 작업을 시작합니다.
  • debounce : action이 dispatch가되면, action을 유지하고 지정된 시간을 기다린 다음 작업을 시작합니다. 기다리는 동안 동일한 action이 dispatch되면 새로운 action을 유지하고 지정된 시간을 기다립니다.

  • 샘플 코드



    문자뿐이라면 「과연, 모르겠습니다」상태가 되기 때문에, 실제로 움직여 보았습니다.

    샘플은 마지막 기사에서 사용한 것에 throttle과 debounce를 추가한 것을 사용했습니다. throttle과 decounce 버튼을 클릭하면 각각 onClickThrottleButton, onClickDebounceButton가 실행됩니다.



    sampleContainer.js
      // 省略
    
      onClickThrottleButton: () => {
        let count = 0
        const interval = setInterval(() => {
        dispatch(throttleSampleStart(count))
          count++
          if(count >= 6) {
            clearInterval(interval)
          }
        }, 500)
      },
      onClickDebounceButton: () => {
        let count = 0
        const interval = setInterval(() => {
        dispatch(debounceSampleStart(count))
          count++
          if(count >= 6) {
            clearInterval(interval)
          }
        }, 500)
      },
    
      // 省略
    

    sampleSaga.js
    // 省略
    
    function* handleThrottleSampleStart() {
      yield throttle(1800, THROTTLE_SAMPLE_START, runThrottleSampleStart)
    }
    
    function* runThrottleSampleStart(action) {
      console.log(`take action ${JSON.stringify(action)}`)
      yield call(sleepAsync, action.payload.count)
      yield put(throttleSampleSuccess())
    }
    
    function* handleDebounceSampleStart() {
      yield debounce(1200, DEBOUNCE_SAMPLE_START, runDebounceSampleStart)
    }
    
    function* runDebounceSampleStart(action) {
      console.log(`take action ${JSON.stringify(action)}`)
      yield call(sleepAsync, action.payload.count)
      yield put(debounceSampleSuccess())
    }
    
    const sleepAsync = async (count) => {
      await new Promise(r => setTimeout(r, 5000))
    }
    
    // 省略
    

    동작 확인



    throttle



    실행 결과는 다음과 같습니다. 우선 dispatch 된 action (payload의 count가 0)로 태스크가 기동해, 1800밀리초 기다립니다. 그 사이에 dispath 된 action(payload의 count가 1, 2, 3)에서는 태스크는 기동하지 않습니다.
    1800 밀리초가 경과한 후, 유지하고 있던 최신의 action(payload의 count가 3)로 다시 태스크를 기동합니다.



    이미지에서 count4의 액션은 작업을 시작하지 않습니다. 이것은, 지정 시간 경과 후에 태스크(count3의 action)가 기동되고 있습니다만, 이 태스크가 기동해도 지정 시간이 경과할 때까지 같은 action을 보관 유지하는 상태가 되어 있기 때문입니다. (약간 ‥)
    count3의 태스크가 기동하고 나서 1800밀리초 이내에 count4와 5의 action이 dispatch 되고 있으므로, 최신의 action(count가 5)가 보관 유지되어 1800밀리초 경과 후에 태스크가 기동되고 있는 것 같습니다 .

    debounce



    실행 결과는 다음과 같습니다. throttle에 비해 다소 간단하네요. 같은 action 가 dispatch 될 때마다 최신의 action 를 보관 유지해 새롭게 지정 초수 기다립니다. 기다리는 동안 같은 작업이 오지 않으면 작업을 시작하고 있습니다.



    요약



    throttle은 takeEvery 대신 사용할 수 있다고 느꼈습니다. takeEvery는 dispatch 된 action을 모두 따기 때문에, 부하의 면에서 조금 걱정이 있습니다만, throttle라면 어느 정도 컨트롤을 할 수 있습니다. 그 때문에, action을 흘리고 싶지 않다, 라고 해서 부하도 많이 걸고 싶지 않다, 라고 하는 장면에서는 차례가 있을 것 같네요.
    debounce는 ‥ 조금 생각하지 않았습니다. 개발을 진행하는 가운데 효과적으로 활용할 수 있는 장면이 나오면 다시 소개하고 싶습니다.

    좋은 웹페이지 즐겨찾기