2016-05-23 8 views
2

Gibt es eine Möglichkeit, in Redux-Saga entprellen, wo nachfolgende Aufrufe in die Warteschlange eingereiht sind hinter der gleichen Verzögerung, die von jeder neuen Aufgabe in der Warteschlange gestoßen wird. Ähnlich wie bei lodash's debunce https://lodash.com/docs#debounce.Redux Saga entprellen und nicht nur verzögern/abbrechen

Ich habe derzeit etwas ähnlich Redox-Sagas Entprellen, aber entfernt den Cancel-Teil, da ich immer noch jede Aufgabe, ich möchte nur alle Ereignisse bündeln in einem einzigen Thread später feuern.

Was ich zur Zeit:

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

Ich versuchte Gabel Variablen zugewiesen wird und dann, wenn die Überprüfung ausgeführt wurde (.isRunning()), aber nicht wissen, wie ich durch eine andere Verzögerung, die Gabel verschieben könnte.

Antwort

1

Ich wollte ein Beispiel mit einem Array als Warteschlange schreiben, um die zu puffernden Aktionen zu speichern, zusammen mit einem setTimeout, um die Warteschlangenaufruf() auf jedem von ihnen zu löschen (und dann das Zeitlimit entsprechend zu löschen, wenn ein neuer Aktion kommt in davor) abläuft, aber ich merkte, dass jetzt Redux-Saga Kanäle unterstützt:

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

sie auch einen eingebauten Puffer haben Aktionen zu speichern, während die Sage beschäftigt ist. Hier besteht der Trick darin, den API-Aufruf aus dem Dokument-Beispiel durch Ihre delay Funktion zu ersetzen, so dass die Saga "beschäftigt" ist und Aktionen für Sie puffern wird.

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

Sie können auch zwischen verschiedenen Pufferstrategien wählen.

Bitte beachten Sie, ich bin nicht 100% sicher, dass mein Beispiel in Ihrem Fall funktioniert, da ich zuvor noch nie Kanäle verwendet habe, aber hoffentlich können Sie es an Ihr Problem anpassen.

2

Wenn Sie Tasks einzeln für die Ausführung planen, werden alle nach einer Entprellzeit ausgelöst, sie werden jedoch nicht in derselben Ereignisschleife gebündelt. Stattdessen wird jeder Verzögerungsaufruf seine Ausführung in einer eigenen Schleife planen. Wenn ich mich nicht irre, möchte ich die gruppierten Aufgaben nach der gleichen Verzögerung in der gleichen Ereignisschleife auslösen.

Die Channel-API bietet eigentlich keine blockierungsfreie Take (und ich denke, Ihr Fall oben schlägt vor, wir sollten es in die Bibliothek hinzufügen). Aber Sie können eine ähnliche Lösung ohne große Schwierigkeiten implementieren.

Eine mögliche Lösung besteht darin, die Arbeit in zwei Daemon Sagas aufzuteilen: Der erste wird fortwährend nach Aktionen Ausschau halten und entprellte Aufgaben in einer gemeinsamen Warteschlange ablegen. Der 2. wird kontinuierlich: 1. für einige Zeit schlafen, 2. aufwachen und gabelt Aufgaben für alle in der Warteschlange befindlichen Aktionen, bis die Warteschlange leer ist, dann wieder schlafen.

Zum Beispiel

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