You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Redux的React SPA长时异步任务WebSocket通信架构问询

Redux架构设计:长时任务 + WebSocket实时更新

这是个非常典型的「后台长任务+前端实时反馈」场景,用Redux Thunk来处理异步逻辑是最优解之一——它允许你的action返回函数,能直接拿到dispatch和getState,完美适配WebSocket这种需要持续监听的异步操作。下面是完整的架构方案和代码实现:

1. 先配置Redux Thunk

首先确保你已经安装了依赖:

npm install redux-thunk
# 或者
yarn add redux-thunk

然后在创建store的时候把thunk作为中间件加入:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);

export default store;

2. 重构Action逻辑

把WebSocket的连接、消息监听、任务结束后的清理逻辑都放到thunk action里,这样能保持reducer的纯函数特性:

// 定义action types(建议单独抽成常量文件)
export const START_LONG_RUNNING_PROCESS = 'START_LONG_RUNNING_PROCESS';
export const PROGRESS_UPDATE_FROM_SERVER = 'PROGRESS_UPDATE_FROM_SERVER';
export const FINISH_LONG_RUNNING_PROCESS = 'FINISH_LONG_RUNNING_PROCESS';
export const TASK_ERROR = 'TASK_ERROR'; // 新增错误处理action

// Thunk action:处理异步流程
export const startLongRunningProcess = () => {
  return async (dispatch) => {
    // 首先分发任务开始的action,显示加载条
    dispatch({ type: START_LONG_RUNNING_PROCESS });

    try {
      // 1. 先向服务器发起长任务请求(比如POST接口)
      await fetch('/api/start-long-task', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        // 如果你需要传参数,这里加body
      });

      // 2. 建立WebSocket连接,监听进度更新
      const socket = new WebSocket('ws://your-server-url/long-task-progress');

      socket.onmessage = (event) => {
        const data = JSON.parse(event.data);
        
        if (data.type === 'progress') {
          // 分发进度更新action
          dispatch({
            type: PROGRESS_UPDATE_FROM_SERVER,
            payload: { progress: data.progress }
          });
        } else if (data.type === 'finish') {
          // 任务完成,分发结束action并关闭WebSocket
          dispatch({ type: FINISH_LONG_RUNNING_PROCESS });
          socket.close();
        }
      };

      // 处理WebSocket连接错误
      socket.onerror = (error) => {
        dispatch({ type: TASK_ERROR, payload: error.message });
        socket.close();
      };

      // 处理WebSocket断开连接
      socket.onclose = () => {
        // 可以在这里做一些清理,比如如果意外断开是否重试?
        console.log('WebSocket connection closed');
      };

    } catch (error) {
      // 处理HTTP请求错误
      dispatch({ type: TASK_ERROR, payload: error.message });
      // 这里可以隐藏加载条
      dispatch({ type: FINISH_LONG_RUNNING_PROCESS });
    }
  };
};

3. 完善Reducer

把错误处理也加进去,同时保持reducer的纯函数特性:

import {
  START_LONG_RUNNING_PROCESS,
  PROGRESS_UPDATE_FROM_SERVER,
  FINISH_LONG_RUNNING_PROCESS,
  TASK_ERROR
} from './actions';

const initialState = {
  showLoadingBar: false,
  progress: 0,
  error: null
};

export default (state = initialState, action) => {
  switch(action.type) {
    case START_LONG_RUNNING_PROCESS: {
      return { 
        ...state, 
        showLoadingBar: true,
        progress: 0,
        error: null // 开始新任务时清空之前的错误
      };
    }
    case PROGRESS_UPDATE_FROM_SERVER: {
      return { 
        ...state, 
        progress: action.payload.progress 
      };
    }
    case FINISH_LONG_RUNNING_PROCESS: {
      return { 
        ...state, 
        showLoadingBar: false 
      };
    }
    case TASK_ERROR: {
      return { 
        ...state, 
        error: action.payload,
        showLoadingBar: false // 出错后隐藏加载条
      };
    }
    default:
      return state;
  }
};

4. 组件中使用

在组件里通过connect连接Redux,然后绑定点击事件:

import React from 'react';
import { connect } from 'react-redux';
import { startLongRunningProcess } from './actions';

const App = ({ showLoadingBar, progress, error, startLongRunningProcess }) => {
  return (
    <div>
      {error && <div className="error">Error: {error}</div>}
      {showLoadingBar && (
        <div className="loading-bar">
          <div style={{ width: `${progress}%` }}></div>
          <p>Progress: {progress}%</p>
        </div>
      )}
      <button onClick={startLongRunningProcess} disabled={showLoadingBar}>
        启动长任务
      </button>
    </div>
  );
};

const mapStateToProps = (state) => ({
  showLoadingBar: state.showLoadingBar,
  progress: state.progress,
  error: state.error
});

const mapDispatchToProps = {
  startLongRunningProcess
};

export default connect(mapStateToProps, mapDispatchToProps)(App);

关键注意点

  • 避免重复连接:给按钮加disabled属性,防止用户多次点击启动多个任务
  • 资源清理:任务完成或出错时一定要关闭WebSocket,避免内存泄漏
  • 错误处理:不要忽略HTTP请求和WebSocket的错误,给用户友好的提示
  • 可扩展性:如果以后需要更复杂的异步流程(比如取消任务),可以考虑用Redux Saga,但Thunk对于这个场景已经足够轻量好用

内容的提问来源于stack exchange,提问作者Andrei Cioara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:55:40