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

如何在Flask中返回202 Accepted且不终止函数?

Flask中实现先返回202 Accepted再处理异步搜索任务的方案

首先明确:HTTP是请求-响应一对一的模型,你没法在同一个请求里先返回202状态码,之后再返回结果。必须通过「异步任务+结果查询」的方式实现需求,以下是两种可行方案:

一、简单场景:用Python线程实现(适合开发/小流量场景)

用threading模块把耗时搜索放到后台线程,先返回202并给用户一个任务ID,让用户后续通过ID轮询结果。

from flask import Flask, jsonify, request
import threading
import time
import uuid

app = Flask(__name__)

# 临时存储任务结果,生产环境建议替换为Redis或数据库
task_results = {}

def long_running_search(task_id, query):
    # 模拟长时间搜索逻辑
    time.sleep(10)
    # 实际场景替换为真实搜索逻辑
    result = f"搜索关键词「{query}」找到100条匹配结果"
    task_results[task_id] = {"status": "completed", "result": result}

@app.route("/search")
def search():
    query = request.args.get("q", "")
    if not query:
        return jsonify({"error": "请提供搜索关键词"}), 400
    
    # 生成唯一任务ID
    task_id = str(uuid.uuid4())
    # 初始化任务状态
    task_results[task_id] = {"status": "processing"}
    
    # 启动后台线程执行搜索
    thread = threading.Thread(target=long_running_search, args=(task_id, query))
    thread.daemon = True  # 线程随应用退出自动结束
    thread.start()
    
    # 先返回202 Accepted,告知用户任务已启动
    return jsonify({
        "task_id": task_id,
        "status": "processing",
        "提示": "搜索任务已启动,可通过下方地址查询结果",
        "结果查询地址": f"/search/result/{task_id}"
    }), 202

@app.route("/search/result/<task_id>")
def get_search_result(task_id):
    task = task_results.get(task_id)
    if not task:
        return jsonify({"error": "任务不存在"}), 404
    
    if task["status"] == "processing":
        return jsonify({"status": "搜索中,请稍等"}), 200
    else:
        # 返回结果后清理临时存储
        result = task["result"]
        del task_results[task_id]
        return jsonify({"status": "completed", "result": result}), 200

if __name__ == "__main__":
    app.run(debug=True)

二、生产环境:用Celery异步任务队列实现

单线程方案在高并发或复杂部署场景下不稳定,推荐用Celery配合Redis/RabbitMQ做消息中间件,更可靠可控。

1. 安装依赖

pip install flask celery redis

2. 代码实现

from flask import Flask, jsonify, request
from celery import Celery
import uuid

app = Flask(__name__)

# 配置Celery连接Redis
app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0'
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'

celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)

@celery.task(bind=True)
def long_running_search(self, query):
    # 模拟长时间搜索
    import time
    time.sleep(10)
    return f"搜索关键词「{query}」找到100条匹配结果"

@app.route("/search")
def search():
    query = request.args.get("q", "")
    if not query:
        return jsonify({"error": "请提供搜索关键词"}), 400
    
    # 提交异步任务到Celery队列
    task = long_running_search.delay(query)
    
    # 返回202 Accepted及任务查询信息
    return jsonify({
        "task_id": task.id,
        "status": "processing",
        "提示": "搜索任务已启动,可通过下方地址查询结果",
        "结果查询地址": f"/search/result/{task.id}"
    }), 202

@app.route("/search/result/<task_id>")
def get_search_result(task_id):
    task = long_running_search.AsyncResult(task_id)
    if task.state == 'PENDING':
        return jsonify({"status": "搜索中,请稍等"}), 200
    elif task.state == 'SUCCESS':
        return jsonify({"status": "completed", "result": task.result}), 200
    else:
        return jsonify({"status": "failed", "提示": "搜索任务执行失败"}), 500

if __name__ == "__main__":
    app.run(debug=True)

关键说明

  • 为什么不能直接在search函数里先返回202再执行任务?因为Flask视图函数返回响应后,当前请求周期就结束了,后续代码不会被执行(不符合HTTP规范)。
  • 轮询方式完全兼容Lynx这类不支持JS的浏览器,用户可以手动访问结果查询地址获取最终数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 13:50:47