Redux-Saga用eventChannel做错误拦截器emit无法触发其他saga问题咨询
问题1:emit不生效的原因和解决方法
你当前的写法只完成了eventChannel的消息生产逻辑,没有对应的消息消费逻辑,emit发送的action没有被saga监听并派发到Redux中,自然无法触发对应logout的处理saga。
正确的修改方式如下:
- 把interceptor改成generator函数,创建channel后持续监听channel内的消息,拿到emit出来的action后通过
put效应派发出去
修改后的代码:
import { eventChannel } from 'redux-saga'; import { put, take, call } from 'redux-saga/effects'; export function* interceptor() { // 创建eventChannel const errorChannel = eventChannel((emit) => { api.interceptors.response.use( (response) => response, (error) => { const { response } = error; // 加一层response存在的判断,避免网络断连等场景报错 if (response && [401, 403].includes(response.status)) { // 把action发送到channel emit(AuthCreators.logoutRequest()); } return Promise.reject(error); } ); return () => null; }); // 持续监听channel的消息,派发到Redux while (true) { const action = yield take(errorChannel); yield put(action); } }
rootSaga的调用逻辑不需要修改,还是直接fork该interceptor即可,这时候emit出来的logout action就会被正常派发,对应的监听该action的saga就能被触发。
问题2:全局错误拦截方案的优劣对比
你当前的方案是可行的,但不算通用最优方案,优缺点非常明确:
- 优点:和请求库拦截器绑定,能拦截所有请求层面的错误,不需要每个业务saga单独写重复的错误判断逻辑
- 缺点:
- 只能拦截请求层面的错误,无法拦截saga内部其他业务逻辑抛出的非请求错误
- 如果多个请求同时返回401/403,会重复派发logout请求,需要额外加防抖逻辑避免重复执行退出逻辑
你可以根据业务场景选择更适配的方案:
- 如果只需要拦截请求错误:修改完问题1的逻辑即可正常使用,额外加一层防抖处理避免重复派发logout请求就足够
- 如果需要拦截所有saga的运行错误:可以封装高阶saga包裹所有业务saga,统一捕获错误,示例代码如下:
function* withErrorHandler(saga, ...args) { try { yield call(saga, ...args); } catch (error) { // 统一错误处理逻辑 const { response } = error; if (response && [401,403].includes(response.status)) { yield put(AuthCreators.logoutRequest()); } else { // 其他错误处理逻辑,比如全局错误提示 yield put(showErrorToast(error.message)); } } } // rootSaga里用高阶saga包裹业务saga export default function* rootSaga() { yield all([ fork(interceptor), fork(withErrorHandler, anotherSaga), fork(withErrorHandler, anotherSaga2) ]); }
内容的提问来源于stack exchange,提问作者Fabio Bueno
相关产品推荐
相关产品推荐

