












In Redux Saga, there are multiple ways to implement polling for API calls, depending on the level of control you desire (such as whether manual stopping is needed, error retry strategies, etc.). Below are several common implementation patterns.
delayThe most direct way: inwhileUsed in a loopdelayControl the interval and call the API each cycle.
import { call, put, delay } from "redux-saga/effects";
function* pollData() {
while (true) {
try {
const data = yield call(api.fetchData);
yield put({ type: "FETCH_SUCCESS", payload: data });
} catch (error) {
yield put({ type: "FETCH_FAILURE", error });
// 可选的错误处理:比如失败后停止轮询或延长间隔
// break; // 停止轮询
}
yield delay(5000); // 等待 5 秒后再进行下一次请求
}
}
Characteristics:
break)take(Listening stop action)If you need an external action (such as the user leaving the page or clicking a stop button) to stop polling, you can add it to the loop.raceortakeTo respond to the stop signal.
import { call, put, delay, take, race } from "redux-saga/effects";
function* pollData() {
while (true) {
const { response, timeout } = yield race({
response: call(api.fetchData),
timeout: delay(5000), // 5秒间隔
});
if (response) {
yield put({ type: "FETCH_SUCCESS", payload: response });
} else {
// 超时后继续等待下一次轮询(其实相当于轮询间隔到了)
}
// 在每次循环末尾检查是否有停止信号
const stopAction = yield take("STOP_POLLING");
if (stopAction) break;
}
}
But the above writing has issues:takeIt will block the loop and cause polling to stop. A better approach is to wait for both conditions simultaneously: the polling interval and the stop signal. You can useraceTo wait for both delay and stop action simultaneously.
import { call, put, delay, take, race } from "redux-saga/effects";
function* pollData() {
while (true) {
// 同时等待 API 调用结果、间隔延迟、停止信号
const { result, stop } = yield race({
result: call(api.fetchData),
stop: take("STOP_POLLING"),
});
if (stop) {
// 收到停止信号,退出循环
break;
}
if (result) {
yield put({ type: "FETCH_SUCCESS", payload: result });
}
// 无论 API 调用成功或失败,都等待下一个间隔
// 可以使用 delay 来控制轮询周期,但注意上面已经用 race 处理了 stop,
// 所以这里再用 delay 会导致 stop 被阻塞。因此更清晰的做法是:
// 在每次循环开头就启动一个 delay,与 API 调用和 stop 并行。
}
}
A more elegant structure is to usefork+cancelto manage stoppable polling (see below).
forkCreate a cancellable polling taskPlace the polling logic in a separate sub-Saga, which the main Saga can use toforkStart it, and when neededcancel.
import { call, put, delay, fork, cancel, take } from "redux-saga/effects";
// 轮询子任务
function* pollTask() {
while (true) {
try {
const data = yield call(api.fetchData);
yield put({ type: "FETCH_SUCCESS", payload: data });
} catch (error) {
yield put({ type: "FETCH_FAILURE", error });
// 可选:发生错误时停止轮询或继续
}
yield delay(5000); // 间隔 5 秒
}
}
// 主 Saga:启动轮询,并监听停止信号
function* mainSaga() {
const poller = yield fork(pollTask);
// 等待停止动作
yield take("STOP_POLLING");
yield cancel(poller);
}
Advantages:
finallyBlocks can execute cleanup logic.If support for "pause/resume" is needed, you canpollTaskInternal joiningtakeListen for pause signals, or use more complex control flow.
Polling usually requires increasing the interval after a failure (exponential backoff) to avoid frequent retries.
import { call, put, delay } from "redux-saga/effects";
function* pollWithBackoff() {
let retryDelay = 1000; // 初始延迟 1 秒
const maxDelay = 60000; // 最大延迟 60 秒
while (true) {
try {
const data = yield call(api.fetchData);
yield put({ type: "FETCH_SUCCESS", payload: data });
retryDelay = 1000; // 成功后重置延迟
} catch (error) {
yield put({ type: "FETCH_FAILURE", error });
// 失败后指数退避,但不超过最大延迟
retryDelay = Math.min(retryDelay * 2, maxDelay);
}
yield delay(retryDelay);
}
}
eventChannelPackaging Polling (Advanced)If polling needs to be combined with other event sources, or if you want to abstract polling into a stream, you can useeventChannel.
import { eventChannel, END } from "redux-saga";
import { call, put, take, cancelled } from "redux-saga/effects";
function createPollingChannel(interval) {
return eventChannel((emitter) => {
const timer = setInterval(async () => {
try {
const data = await api.fetchData();
emitter({ type: "DATA", payload: data });
} catch (error) {
emitter({ type: "ERROR", error });
}
}, interval);
// 取消函数
return () => {
clearInterval(timer);
emitter(END);
};
});
}
function* pollWithChannel() {
const channel = yield call(createPollingChannel, 5000);
try {
while (true) {
const action = yield take(channel);
yield put(action); // 将 channel 发出的动作直接 put 到 store
}
} finally {
if (yield cancelled()) {
channel.close();
}
}
}
This approach is more flexible but has higher complexity, suitable for scenarios where polling needs to be integrated with systems other than Redux.
| Demand | Recommended method |
|---|---|
Usually,Method 3 (fork+cancel)It is the most practical and scalable solution, maintaining the independence of polling while offering flexible external control.
This content is automatically aggregated by InertiaRSS (RSS Reader) for reading reference only. Original from — Copyright belongs to the original author.