3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

redux-sagaのthrottleとdebounceを試してみる

Posted at

はじめに

redux-sagaのAPI referenceを眺めていて、読んだだけでは動きがイメージしにくかったthrottleとdebounceについて、実際に動かしてイメージを掴んでみました。(英語難しい)
実際の開発で使用したわけではないので、サンプルを作って動かしてみただけになります。サンプルのソースはこちらからどうぞ。

使用したredux-sagaのバージョン: 1.0.5

TL;DR

基本的にはどちらのAPIもtakeと同様にactionのdispatchを待ち受けてタスクを起動します。同じactionが短時間内に複数dispatchされた際の挙動が異なります。

  • throttle: actionがdispatchされたらタスクを起動します。指定時間内に同じactionがdispatchされた場合はタスクを起動せずに最新のactionを1個だけ保持しておき、指定時間経過後にタスクを起動します。
  • debounce: actionがdispatchがされたら、actionを保持して指定時間待ってからタスクを起動します。待っている間に同じactionがdispatchされた場合は、新しいactionを保持してまた指定時間待ちます。

サンプルコード

文字だけだと「なるほど、わからん」状態になるので、実際に動かしてみました。

サンプルは前回の記事で使用したものにthrottleとdebounceを追加したものを使用しました。throttleとdecounceのボタンをクリックしたらそれぞれonClickThrottleButtononClickDebounceButtonが実行されます。

スクリーンショット 2019-09-29 12.02.09.png

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)で再度タスクを起動します。

スクリーンショット 2019-09-29 12.13.09.png

画像ではcount4のactionではタスクが起動されていません。これは、指定時間経過後にタスク(count3のaction)が起動されていますが、このタスクが起動してからも指定時間が経過するまで同じactionを保持する状態になっているからです。(ややこしい‥)
count3のタスクが起動してから1800ミリ秒以内にcount4と5のactionがdispatchされているので、最新のaction(countが5)が保持されて1800ミリ秒経過後にタスクが起動されているようです。

debounce

実行結果は以下になります。throttleと比較していくらかシンプルですね。同じactionがdispatchされる度に最新のactionを保持して新たに指定秒数待ちます。待っている間に同じactionが来なかった場合にタスクを起動しています。

スクリーンショット 2019-09-29 12.35.28.png

まとめ

throttleはtakeEveryの代わりに使えそうだと感じました。takeEveryはdispatchされたactionをすべて拾うので、負荷の面でちょっと心配がありますが、throttleならある程度コントロールができます。そのため、actionを取りこぼしたくない、かといって負荷もあまりかけたくない、といった場面では出番がありそうですね。
debounceは‥ちょっと思いつきませんでした。開発を進める中で有効に活用できる場面が出てきたらまた紹介したいと思います。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?