redux-saga 中的重试功能

retry functionality in redux-saga

在我的应用程序中,我有以下代码

componentWillUpdate(nextProps) {
  if(nextProps.posts.request.status === 'failed') {
    let timer = null;

    timer = setTimeout(() => {
      if(this.props.posts.request.timeOut == 1) {
        clearTimeout(timer);
        this.props.fetchData({
          page: this.props.posts.request.page
        });
      } else {
        this.props.decreaseTimeOut();
      }
    }, 1000);
  }
}

它的作用是,当 API 请求遇到错误时,可能是因为没有互联网连接(比如 facebook 的聊天方式),或者后端有错误,它会五秒后重试,但是 setTimeout 需要每隔一秒设置一次以更新存储的一部分,即行 this.props.decreaseTimeOut();,但是如果计数器有 运行,那么五秒钟过去了,if block 将 运行 并重新发送 fetchData action

它运行良好,我对它没有任何问题,至少在功能方面,但在代码设计方面,我知道它是一个 side-effect,它不应该在我的反应中处理-组件,并且由于我使用的是 redux-saga(但我是 redux-saga 的新手,我今天刚学会),我想将该功能转换为传奇,我不太清楚如何还没有做到这一点,顺便说一句,这是我的 fetchData saga

import {
  take,
  call,
  put
} from 'redux-saga/effects';

import axios from 'axios';

export default function* fetchData() {
  while(true) {
    try {
      let action = yield take('FETCH_DATA_START');
      let response = yield call(axios.get, '/posts/' + action.payload.page);
      yield put({ type: 'FETCH_DATA_SUCCESS', items: [...response.data.items] });
    } catch(err) {
      yield put({ type: 'FETCH_DATA_FAILED', timeOut: 5 });
    }
  }
}

对您的代码而言,侵入性较小的事情是使用来自 redux-saga 的 delay promise:

catch(err) {
   yield put({ type: 'FETCH_DATA_FAILED'});

   for (let i = 0; i < 5; i++) {
       yield call(delay, 1000);
       yield put(/*Action for the timeout/*);
  }
}

但我会以这种方式重构您的代码:

function* fetchData(action) {
    try {
      let response = yield call(axios.get, '/posts/' + action.payload.page);
      yield put({ type: 'FETCH_DATA_SUCCESS', items:[...response.data.items] });
    } catch(err) {
      yield put({ type: 'FETCH_DATA_FAILED'});
      yield put({ type: 'SET_TIMEOUT_SAGA', time: 5 });
    }
  }
}

function *setTimeoutsaga(action) {
   yield put({type: 'SET_STATE_TIMEOUT', time: action.time}); // Action that update your state
   yield call(delay, 1000);

   // Here you use a selector which take the value if is disconnected:
   // https://redux-saga.js.org/docs/api/#selectselector-args
   const isStillDisconnected = select() 
   if (isStillDisconnected) {
       yield put({type: 'SET_TIMEOUT_SAGA', time: action.time - 1});
}

function *fetchDataWatchers() {
    yield takeEvery('FETCH_DATA_START', fetchData);
    yield takeEvery('SET_TIMEOUT_SAGA', setTimeoutSaga);

    // You can insert here as many watcher you want
}

export default [fetchDataWatchers]; // You will use run saga for registering this collection of watchers