如何定期调用redux-saga?实现定时数据获取的saga方案
你的核心问题在于:手动调用生成器的next()方法无法让Redux-Saga中间件处理内部的yield指令——你在createTimer里嵌套的生成器并没有被Saga中间件接管,所以里面的yield fetchData()根本不会被执行,只是返回了一个未运行的生成器对象。
Redux-Saga本身提供了专门的效果(Effect)来处理异步定时逻辑,完全不需要手动管理setInterval和生成器的next()调用。下面是重构后的完整方案:
重构思路
- 用Saga内置的
delay效果替代setInterval,配合while(true)循环实现定时任务 - 用
race效果监听取消信号(比如登出、认证失败),自动终止定时任务 - 利用
takeLatest自动取消之前的定时任务,避免重复触发
完整代码
// @flow import { takeLatest, all, put, select, delay, race, take } from 'redux-saga/effects'; import * as types from '../constants/ActionTypes'; const getUpdateInterval = (state) => state.settings.updateInterval; export default function* data(): Generator<any, any, any> { yield all([ takeLatest(types.AUTHENTICATE_SUCCEEDED, startPeriodicFetch), takeLatest(types.LOGGED_OUT, stopPeriodicFetch), takeLatest(types.AUTHENTICATE_FAILED, stopPeriodicFetch) ]); } function* fetchData() { yield put({ type: types.FETCH_DATA }); } function* startPeriodicFetch() { const updateInterval = yield select(getUpdateInterval); // 启动定时任务,同时监听取消信号 yield race([ // 循环执行定时任务 (function* () { while (true) { yield fetchData(); yield delay(updateInterval * 1000); } })(), // 等待取消action,触发后终止race take([types.LOGGED_OUT, types.AUTHENTICATE_FAILED]) ]); } function* stopPeriodicFetch() { // 这里不需要额外逻辑,因为takeLatest会自动取消之前的startPeriodicFetch任务 // race中的循环会被中断,delay也会被取消 }
关键细节解释
为什么不用手动管理定时器?
Redux-Saga的delay是一个阻塞效果,会自动被中间件管理。当saga任务被取消(比如takeLatest触发新任务时),delay会立即终止,同时while循环也会停止,完全不需要手动调用clearInterval。race效果的作用race会同时运行多个子任务,当其中任意一个任务完成时,其他任务会被自动取消。这里我们让定时循环和取消信号“赛跑”,一旦收到登出或认证失败的action,定时任务就会立即终止。takeLatest的自动取消
当用户多次触发AUTHENTICATE_SUCCEEDED时,takeLatest会自动取消之前的startPeriodicFetch任务,避免同时运行多个定时任务。
为什么你的原代码不工作?
在createTimer里,你手动调用了callback.next(),但这个生成器并没有被Redux-Saga中间件运行。Saga中间件需要通过yield来执行生成器,才能处理里面的yield put、yield select等效果。手动调用next()只会让生成器执行到下一个yield,但不会触发中间件的处理逻辑,所以fetchData里的put动作根本不会被分发到store。
内容的提问来源于stack exchange,提问作者Brandon

