如何实现Flask后端任务完成后向React.js前端触发回调函数
回答
完全可以实现,因为HTTP默认是单向短连接,没法直接支撑30分钟长链路的回调,你可以从以下3种成熟方案里选适合你当前技术栈的实现:
方案1:WebSocket 双向通信(最灵活的方案)
WebSocket 支持前后端全双工通信,后端可以随时主动给前端推送消息,适合需要双向交互的场景。
实现步骤:
- Flask端安装
flask-socketio库扩展WebSocket能力,React端安装socket.io-client对接 - 前端触发任务时先建立WebSocket连接,生成唯一的任务ID和当前连接绑定
- Flask收到任务请求后,将任务投递到后台任务队列(如Celery、RQ,不要直接在请求线程执行,否则会超时阻塞)
- 任务执行完成后,Flask通过绑定的WebSocket连接向前端推送完成状态和结果,前端收到消息后触发对应回调函数即可
核心代码示例:
Flask端核心逻辑:
from flask import Flask, request from flask_socketio import SocketIO, emit from celery import Celery app = Flask(__name__) app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0' app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0' socketio = SocketIO(app, cors_allowed_origins="*") celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL']) celery.conf.update(app.config) @celery.task def long_time_task(params): # 你的30分钟长耗时逻辑写在这里 return "任务执行完成,结果为xxx" @app.route('/trigger-task', methods=['POST']) def trigger_task(): params = request.json task = long_time_task.delay(params) return {"task_id": task.id} @socketio.on('bind_task') def bind_task(task_id): # 后台监听任务状态,完成后推送 while True: task = long_time_task.AsyncResult(task_id) if task.state == 'SUCCESS': emit('task_finished', task.result) break socketio.sleep(10)
React端核心逻辑:
import { io } from 'socket.io-client' function App() { const triggerTask = async () => { // 1. 触发任务拿到任务ID const res = await fetch('你的Flask服务地址/trigger-task', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({/* 你的自定义参数 */}) }) const { task_id } = await res.json() // 2. 建立WebSocket绑定任务,监听完成通知 const socket = io('你的Flask服务地址') socket.emit('bind_task', task_id) socket.on('task_finished', (result) => { // 这里写你要触发的回调逻辑 console.log('任务完成', result) }) } return <button onClick={triggerTask}>触发长耗时任务</button> }
方案2:SSE(服务器发送事件,轻量单向推送方案)
SSE是HTTP原生支持的单向推送机制,只需要服务端向前端推送消息的场景下,比WebSocket更轻量,前端原生支持不需要额外依赖。
实现逻辑:
- 前端触发任务后拿到返回的任务ID,用原生
EventSource接口建立SSE连接 - Flask端实现SSE接口,挂起连接后监听对应任务的执行状态,任务完成后将结果通过SSE通道推送给前端,前端收到消息后触发回调。
方案3:前端轮询(新手最容易上手的方案)
如果觉得前两种方案的通信逻辑太复杂,30分钟的长耗时任务完全可以用轮询实现,开发成本最低。
实现逻辑:
- 前端触发任务后,后端立刻返回唯一的任务ID
- 前端按固定间隔(比如1分钟,30分钟的任务完全不会有太大请求量)向后端发送请求查询该任务的执行状态
- 后端查询任务队列返回当前状态,前端收到
成功状态时触发回调即可
优势:
- 不需要学习额外的通信协议,代码逻辑直白不容易踩坑
- 天然支持页面刷新场景,只要把任务ID存在本地存储,刷新后还能接着查询任务进度
注意事项
- 所有方案都需要把长耗时任务放到后台任务队列执行,绝对不要直接写在Flask的路由处理函数中,Flask默认的单线程处理模式会直接阻塞请求,还会触发网关超时。
- 建议对任务状态做持久化存储,即使用户中途关闭页面,下次打开还可以追溯任务结果。
内容的提问来源于stack exchange,提问作者Sahan Poornaka
相关产品推荐
相关产品推荐

