function* fetchData(){
while(true){
const {payload} = yield take('fetchData')
yield call(func,...) // here, mySaga func cannot take actions until yield call finishes, so actions that are dispatched in-between will lost
// therefore, better to use yield fork instead call, like this
yield fock(func,...) //yield fork is a non-blocking method
}
}
An other solution, we can use takeEvery to take actions simultaneously.
takeEvery allows multiple actions(like fetchData) instances to be started concurrently. At a given moment, we can start a new fetchData task while there are still one or more previous fetchData tasks which have not yet terminated.
If we want to only get the response of the latest request fired (e.g. to always display the latest version of data) we can use the takeLatest.
takeLatest allows only one fetchData task to run at any moment. And it will be the latest started task. If a previous task is still running when another fetchData task is started, the previous task will be automatically cancelled.
Setting up your root Saga (global error handling)
V1.0 yield all implementation
export default function* rootSaga() {
yield all([
helloSaga(),
watchIncrementAsync()
])
// code after all-effect
}
Here, the all effect is used with an array and your sagas will be executed in parallel.
V2.0 other common root implementation
export default function* root() {
yield fork(saga1)
yield fork(saga2)
yield fork(saga3)
// code after fork-effect
}
This is one popular implementation that behaves similarly to the tutorial root Saga behavior.
fock is non-blocking and so allows the rootSaga in these two cases to finish while the child sagas are kept running and blocked by their internal effects.
DIFFERENCE all effect is blocking, so code after all is executed after all the children sagas completes, while several fork effects are non-blocking and code after fork effect gets executed right after yielding fork effects. Another difference is that you can get task descriptors when using fork effects, so in the subsequent code you can cancel/join the forked task via task descriptors.
V3.0 Nesting fork effects in all effect
const [task1, task2, task3] = yield all([ fork(saga1), fork(saga2), fork(saga3) ])
By doing so, you can get an array of task descriptors.
Uncaught errors from forked tasks bubble to the parent task and thus abort it (and all its child tasks) - they cannot be caught by the parent task.
4.0 Keeping the root alive
Implementations above aren’t terribly practical because rootSaga will terminate on the first error in any individual child effect or saga and crash the whole app!
USE spawn: spawn is an effect that will disconnect your child saga from its parent, allowing it to fail without crashing it’s parent.
The spawn effect might be considered similar to Error Boundaries in React in that it can be used as extra safety measure at some level of the saga tree, cutting off a single feature or something and not letting the whole app crash.
export default function* root() {
yield spawn(saga1)
yield spawn(saga2)
yield spawn(saga3)
}
5.0 Keeping the root alive
To make sagas to be able to restart in the event of failure.
function* rootSaga () {
const sagas = [
saga1,
saga2,
saga3,
];
yield all(
sagas.map((saga) => {
return spawn(function*() {
while (true) {
try {
yield call(saga)
break
} catch (e) {
if (__DEV__) {
/*eslint-disable no-console*/
console.warn('rootSaga catch an error! ', e)
}
}
yield delay(1000); // Optional. Avoid infinite failures blocking app TODO use backoff retry policy...
}
})
})
)
}
one example for saga error handling
- Saga Error Handling Example #644
- Considering simultaneous same errors when restarting sagas, so we use
setTimeoutandisSyncErrorflag. - Another idea, we can set
maximum restart timeas a protect mechanism.
export default function* root() {
yield fork(startup)
const sagas = [nextRedditChange, invalidateReddit]
yield sagas.map(saga =>
spawn(function* () {
let isSyncError = false
while (!isSyncError) {
isSyncError = true
try {
setTimeout(() => isSyncError = false)
yield call(saga)
} catch (e) {
if (isSyncError) {
throw new Error(saga.name + ' was terminated because it threw an exception on startup.')
}
yield put(actions.setError(e.message))
}
}
})
)
}
How to create query cancel timeout
use race . REF
function* sendRequest(request) {
try {
const res = yield call(api, request)
return { res }
} catch (err) {
return { err }
}
}
function* handleRequest(request) {
const task = yield fork(sendRequest, request)
const { res, timeout } = yield race({
res: join(task),
timeout: call(delay, 10 * 1000)
})
if (timeout) {
yield put({ type: 'REQUEST_TIMEOUT' })
return
}
if (res.err) {
yield put({ type: 'REQUEST_ERROR', err: res.err })
return
}
yield put({ type: 'REQUEST_SUCCESS', result: res.res })
}
本文探讨了如何设置根Saga进行全局错误处理,包括V1.0至V5.0的不同实现策略,如使用`all`和`fork`效果来控制并发。同时,介绍了如何通过`takeLatest`确保只响应最新的请求,并防止错误导致应用崩溃。还提供了一个Saga错误处理的实例,讨论了在重启sagas时处理相同错误的方法和取消查询超时的技巧。
8084

被折叠的 条评论
为什么被折叠?



