2016-05-23 6 views
2

Redux-Sagaでデバウンスを行う方法はありますか?後続のコールが同じ遅延の後ろにキューイングされます。キューに追加された新しいタスクごとにバンプが発生し続けます。ロダッシュのデバウンスhttps://lodash.com/docs#debounceに似ています。遅延/キャンセルだけでなく、Redux Sagaデバウンス

私は現在redux-sagaのデバウンスに類似していますが、まだ各タスクを実行したいのでキャンセル部分を削除しました。後で単一スレッドですべてのイベントをまとめてバンドルしたいだけです。

私は現在持っているもの:

const deferTime = 2000; 
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); 

export function* sendClickEvent (event, debounce) { 
    if (debounce) { 
    yield call(delay, deferTime); 
    } 
    yield put(action(event)); 
} 

export function* clickSaga() { 
    while (true) { 
    const action = yield take(WIDGET_CLICKED); 
    const state = yield select(); 
    const debounce = action.meta && action.meta.debounce; 
    const payload = getWidgetClickPayload(state, action); 
    const defaultData = getDefaultData(state); 
    const event = { 
     name: payload.name, 
     data: Object.assign(defaultData, payload.data) 
    }; 
    yield fork(sendClickEvent, event, debounce); 
    } 
} 

私は変数にフォークを割り当て、それが実行されていたならば、チェックしてみました(.isRunning())が、私は別の遅延によってそのフォークを延期することができる方法を知りませんでした。

答えて

1

私はバッファにアクションを格納するためにキューとして配列を使用する例を書こうとしていましたが、setTimeoutを使用して、それぞれにcall()を呼び出すキューをフラッシュしました。有効期限が切れる前にアクションが)入ってくるが、私は今、Reduxの-サガはチャンネルをサポートしていることに気づい:

https://yelouafi.github.io/redux-saga/docs/advanced/Channels.html

彼らはまた、内蔵されているバッファサガがビジー状態の間、アクションを格納します。ここでは、ドキュメントの例からのapi呼び出しをdelay関数に置き換えて、サガが「ビジー」で、アクションをバッファリングするようにします。

const deferTime = 2000; 
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); 

export function* sendClickEvent (event) { 
    yield put(action(event)); 
} 

export function* clickSaga() { 
    // Create a channel (buffered by default) 
    const requestChan = yield actionChannel(WIDGET_CLICKED) 

    while (true) { 
    // Note: we now take actions from the channel (buffered) 
    const action = yield take(requestChan) 

    const state = yield select(); 
    const debounce = action.meta && action.meta.debounce; 
    const payload = getWidgetClickPayload(state, action); 
    const defaultData = getDefaultData(state); 
    const event = { 
     name: payload.name, 
     data: Object.assign(defaultData, payload.data) 
    }; 
    // This should "suspends" the saga and makes it buffer events. 
    yield call(delay, deferTime) 

    yield fork(sendClickEvent, event); 
    } 
} 

また、さまざまなバッファ戦略を選択できます。

私は以前あなたのチャンネルを使ったことがないので、あなたのケースで私の例がうまくいくとは思っていませんが、うまくいけばあなたの問題にそれを適応させることができます。

2

実行のためにタスクを個別にスケジュールすると、デバウンス期間後にすべて起動しますが、同じイベントループではバンドルされません。代わりに、各遅延呼び出しはそれ自身のループでその実行をスケジュールする。もし私が間違っていないなら、あなたが望むのは同じ遅延後に同じイベントループでグループ化されたタスクを起動することです。

チャネルAPIは実際には非ブロックテイクを提供しません(私はあなたのケースではライブラリに追加することを提案していると思います)。しかし、それほど難なく同様のソリューションを実装することができます。

可能な解決策は、作業を2つのデーモンサガに分割することです.1人は継続してアクションを監視し、デバウンスされたタスクを共有キューに入れます。 2番目は継続的に次のようになります。1.しばらくの間スリープします。2.待ち行列が空になるまで、すべての待ち行列に入れられたアクションに対して目を覚ましてフォークします。例えば

import { delay } from 'redux-saga' 
import { take, put, call, fork, select } from 'redux-saga/effects' 

const deferTime = 2000; 

function* clickSaga() { 
    const taskQueue = [] 
    // fork the worker tasks 
    yield fork(worker, taskQueue) 
    while (true) { 
    const action = yield take(WIDGET_CLICKED); 
    const state = yield select(); 
    const debounce = action.meta && action.meta.debounce; 
    const payload = getWidgetClickPayload(state, action); 
    const defaultData = getDefaultData(state); 
    const event = { 
     name: payload.name, 
     data: Object.assign(defaultData, payload.data) 
    }; 

    if(debounce) { 
     // debounce? batch execution 
     taskQueue.push({ task: sendClickEvent, event}); 
    } else { 
     // no debounce, execute right now 
     yield fork(sendClickEvent, event) 
    } 

    } 
} 

function* worker(queue) { 
    while(true) { 
    // sleep 
    yield call(delay, deferTime) 
    // after wakeup, flush the batched tasks 
    let current 
    while(current = queue.shift()) { 
     const {task, event} = current 
     yield fork(task, event) 
    } 
    } 
} 
関連する問題