saga笔记

本文探讨了如何设置根Saga进行全局错误处理,包括V1.0至V5.0的不同实现策略,如使用`all`和`fork`效果来控制并发。同时,介绍了如何通过`takeLatest`确保只响应最新的请求,并防止错误导致应用崩溃。还提供了一个Saga错误处理的实例,讨论了在重启sagas时处理相同错误的方法和取消查询超时的技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ref

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 spawnspawn 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 setTimeout and isSyncError flag.
  • Another idea, we can set maximum restart time as 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 })
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值