-->

redux saga, throttle/debounce conditionally?

2019-02-15 05:18发布

问题:

I'm logging banner impressions when it is visible on screen.

When a user scrolls, the same banner can be made visible multiple times in short time period.

And I'd like to prevent that.

On first thought, throttle is perfect way of preventing it.

But then when you have multiple banners in one page, throttle would not log the 2nd banner in screen if throttled.

So how can I throttle per key? (banner id as a key in this example) ie, I want to throttle banner impression per banner_id. (it's like servers throttling api_endpoint access per api keys)

EDIT

One could think of creating throttle for each key, but wonder if it could take up too much resources?

I wonder how API libraries such as Django-rest-framework implements throttle per api key. I guess it could be totally different from what saga throttle does.

回答1:

Using an expirable map

Things like django-rest use expirable map for throttling. An expirable set is sufficient for the task, too.

I cannot recommend an exact npm module for expirable map/set, unfortunately.

The pseudocode:

function* throttlePerKey(pattern, selector, timeout, saga) {
  const set = new ExpirableSet({ expire: timeout })

  while(true) {
    const action = yield take(pattern)
    const id = selector(action)
    const throttled = set.has(id)
    if (throttled) {
      // Do nothing, action throttled
    } else {
      set.add(id)
      yield call(saga, action)
    }
  }
}

Using only redux-saga

We can emulate an expirable set with redux-saga, and get a purely redux-saga solution.

The code:

function* throttlePerKey(pattern, selector, timeout, saga) {
  const set = new Set()

  while(true) {
    const action = yield take(pattern)
    const id = selector(action)
    const throttled = set.has(id)
    if (throttled) {
      // Do nothing, action throttled
    } else {
      set.add(id)
      // Expire items after timeout
      yield fork(function* () {
        yield delay(timeout)
        set.delete(id)
      })
      yield call(saga, action)
    }
  }
}


标签: redux-saga