InertiaRSS Track and read blogs, news, and tech you care about
Read Original Open in InertiaRSS

Recommended Feeds

罗磊的独立博客
月光博客
月光博客
宝玉的分享
宝玉的分享
S
SegmentFault 最新的问题
WordPress大学
WordPress大学
小众软件
小众软件
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
aimingoo的专栏
aimingoo的专栏
V
V2EX
人人都是产品经理
人人都是产品经理
Recent Announcements
Recent Announcements
V
Visual Studio Blog
云风的 BLOG
云风的 BLOG
MyScale Blog
MyScale Blog
Hugging Face - Blog
Hugging Face - Blog
阮一峰的网络日志
阮一峰的网络日志
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
Jina AI
Jina AI
博客园 - 司徒正美

博客园 - 箫笛

windows - WSL 的安装与使用 shell编程 - dialog 程序使用指南 FE Team - 如何做好前端代码审查 git 提交的撤销和恢复 React15 - React CSS Modules BEM命名实践 React15 - React 15 中 componentWillReceiveProps 为什么会多次调用, 同时componentDidUpdate 也会多次调用? React15 - React15类组件多次执行render方法的原因? React15 - React15应用中代码逻辑复用方案 React15 - React状态同步问题解决 React15 - React 15 中 React.pureComponent 的使用场景 React15 - React 15应用在页面渲染时会多次执行类组件的render 函数的原因 React15 - React 15 中能用 componetDidUpdate 代替 componentWillReceiveProps 吗? React15 - React 15 生命周期函数详解 React15 - 如何在React 15中实现自定义的事件订阅与发送(例如组件间通信) React15 - React15应用中的事件订阅和发送机制 React15 - CSS中的BEM规范 React15 - React CSS Modules BEM命名实践 React15 - 写sass 样式文件,嵌套的结构好,还是扁平的结构好? React15 - sass 中 @mixin 和 @extend 的区别是什么? React15 - React 15 应用 如何使用Css moudules 方式进行模块化开发 React15 - React15应用Sass使用指南 React15 - React 15 应用如何进行性能优化?
React15 - How to implement polling interface calls in redux-saga?
箫笛 · 2026-03-26 · via 博客园 - 箫笛

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.

1. Simple loop +delay

The 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:

  • Simple and intuitive, it will keep running until the Saga is cancelled or the loop is terminated (e.g.,break)
  • Unable to flexibly stop from the outside (unless canceling the entire Saga).

2. Polling that can be stopped (usingtake(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).

3. UseforkCreate a cancellable polling task

Place 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:

  • The polling logic is clearly separated.
  • Can be canceled at any time, supports multiple polling tasks in parallel.
  • After cancellation, the subtask infinallyBlocks can execute cleanup logic.

If support for "pause/resume" is needed, you canpollTaskInternal joiningtakeListen for pause signals, or use more complex control flow.

4. Error retry with exponential backoff

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);
  }
}

5. UseeventChannelPackaging 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.

Summary and Selection Recommendations

Simple fixed-interval polling, no external stop Method 1:while+delayManual stop required (e.g., user exit) Method 3:fork+cancelNeed to handle pause/resume in polling Can be added to the subtask of method 3takemonitorNeed Intelligent Retry (Exponential Backoff) Method 4: Error Retry LogicThe polling logic needs to be integrated with complex event streams. Method 5:eventChannel
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.