generator with 'takeLatest' - is canceling and debouncing possible?
alp82 opened this issue · comments
You showed an example with generators:
const machine = Machine.create('todo-app', {
state: { name: 'idle', todos: [] },
transitions: {
'idle': {
'fetch todos': function * () {
yield { name: 'fetching' };
try {
const todos = yield call(getTodos, '/api/todos');
} catch (error) {
return { name: 'error', error };
}
return { name: 'done', todos };
}
}
}
});
If I would perform machine.fetchTodos()
twice while the async call is still ongoing, the second one is simply discarded.
- How to model a machine that cancels the first one and fetches again?
- How to debounce such executions?
I'll try to release a new version these days. Funny enough I hit the same problem. I just needed takeLatest
.
I just stumbled upon this project due to your great article and this was the first thing that came to my mind when taking a glance at your README.
Yep, that's definitely a valid use case. I'm going to add something which will work like takeLatest
.
Ok, just released 3.0.0
version that supports a latest version of the method. So for example if the machine has a fetchTodos
method you may use:
machine.fetchTodos.latest();
This will kill a previous call which is still in progress. However, I have to make a note here. Let's see the following redux-saga code:
function test (n) {
return {
type: 'TEST',
n
};
}
store.runSaga(function * () {
yield takeLatest('TEST', function * ({ n }) {
console.log(n);
setTimeout(() => console.log('you can not stop me'), 2000);
console.log('promise: ' + (yield call(a, n)));
});
});
store.runSaga(function *() {
yield put(test(1));
yield put(test(2));
});
/*
1 <-- immediately
2 <-- immediately
promise 2 <-- a second later
you can not stop me <-- two seconds later
you can not stop me <-- two seconds later
*/
So we have a saga that waits for an action. Once this action comes we do something (the first console.log
) synchronous and then do two other things which are asynchronous. The difference between them is that the first one setTimeout(() => console.log('you can not stop me'), 2000);
is not in a control of redux-saga library while the second one is. As we can see from the produced console output we get that first async operation fired no matter what. That is because redux-saga has no idea about this process and can not stop it. It is the same with Stent. As long as the async process is wrapped in a call
execution we have .latest
working. Otherwise then canceling does not work.
That new version of Stent support takeLatest
-ish logic which is fine for cases where we don't bother making the request twice. However if you want to implement a debounce and avoid making multiple requests I'll suggest to do it upfront and not rely on Stent for it. Just debounce the action dispatching.