2016-05-23 62 views
2

有沒有辦法在Redux-Saga中反彈,後續的呼叫是在相​​同的延遲後面排隊的,每次新的任務都會加入隊列。類似於lodash的反彈https://lodash.com/docs#debounceRedux傳奇反彈而不僅僅是延遲/取消

我目前有一些類似於redux-saga的debounce,但刪除了取消部分,因爲我仍然想要執行每個任務,我只想捆綁所有事件以便在稍後的單個線程中觸發。

我有什麼當前:

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()的隊列(然後在相應的時候取消超時行動中來到期前),但我注意到,現在終極版 - 佐賀支持渠道:

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

他們也有一個內置的緩存來存儲行爲而傳奇忙。這裏的訣竅是用你的delay函數替換文檔示例中的api調用,這樣這個傳奇是「忙碌」的並且會爲你緩衝動作。

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); 
    } 
} 

您也有不同的緩衝策略可供選擇。

請注意我並不是100%確定我的例子會適用於您的情況,因爲我以前從未使用過渠道,但希望您可以將其適應您的問題。

2

如果您單獨安排任務執行,它們會在去抖週期後全部激活,但它們不會被捆綁在同一個事件循環中;相反,每個延遲呼叫都會將其執行安排在自己的循環中。如果我沒有弄錯,你想要的是在相同的延遲後在相同的事件循環中觸發分組任務。

通道API實際上並不提供非阻塞取(我認爲你的情況上面提示我們應該將它添加到庫中)。但是您可以毫不費力地實施類似的解決方案。

一個可能的解決方案是將工作分成2個守護進程:第1個將持續監視動作並將反彈的任務放入共享隊列中。第二種情況會持續: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) 
    } 
    } 
}