如何用jQuery实现加载轮,在Python后台进程完成后自动隐藏?
问题描述
需要实现一个加载轮,在后台数据摄入流程运行时显示,流程完成后自动隐藏。数据摄入流程包括:爬取维基百科页面并转换为文本文件,再导入LLM模型。使用Flask做前后端通信,曾尝试用ajaxComplete处理POST请求,但加载轮仅显示毫秒级时长,目前采用轮询方式实现,现有代码如下:
现有HTML代码
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <style> #loading { display: none; } </style> <div class="card-footer"> <form id="ingestArea" class="input-group"> <input type="text" id="html" name="url" placeholder="Type your message..." autocomplete="off" class="form-control type_msg" required/> <div class="input-group-append"> <button type="submit" id="feed" class="input-group-text send_btn"><i class="fas fa-location-arrow"></i></button> </div> <div id="loading"><img src="https://i.pinimg.com/originals/36/3c/2e/363c2ec45f7668e82807a0c053d1e1d0.gif"></div> </form> </div> <script> $(document).ready(function() { var interval; $("#messageArea").on("submit", function(event) { const date = new Date(); const hour = date.getHours(); const minute = date.getMinutes(); const str_time = hour+":"+minute; var rawText = $("#text").val(); var userHtml = '<div class="d-flex justify-content-end mb-4"><div class="msg_cotainer_send">' + rawText + '<span class="msg_time_send">'+ str_time + '</span></div><div class="img_cont_msg"><img src="https://i.ibb.co/d5b84Xw/Untitled-design.png" class="rounded-circle user_img_msg"></div></div>'; $("#text").val(""); $("#messageFormeight").append(userHtml); $.ajax({ data: { msg: rawText, }, type: "POST", url: "/get", }).done(function(data) { var botHtml = '<div class="d-flex justify-content-start mb-4"><div class="img_cont_msg"><img src="https://i.ibb.co/fSNP7Rz/icons8-chatgpt-512.png" class="rounded-circle user_img_msg"></div><div class="msg_cotainer">' + data + '<span class="msg_time">' + str_time + '</span></div></div>'; $("#messageFormeight").append($.parseHTML(botHtml)); }); event.preventDefault(); }); $("#ingestArea").on("submit",function(event) { var rawText = $("#html").val(); $('#loading').show(); interval = setInterval(checkStatus, 1000); $("#html").val(""); $.ajax({ data:{ url: rawText, }, type: "POST", url: "/ingest", }); }); }); function checkStatus() { $.ajax({ url: '/check_status', type: 'GET', success: function(response) { if (response.status === "in_progress") { $('#loading').show(); } else{ clearInterval(interval); $('#loading').hide(); } }, error: function(xhr) { clearInterval(interval); $('#loading').hide(); } }); </script>
现有Python(Flask)代码
is_ingesting = False @app.route('/check_status', methods=['GET']) def check_status(): global is_ingesting if is_ingesting: return jsonify(status="in_progress") else: return jsonify(status="completed") def ingest(): global is_ingesting is_ingesting = True start_url = request.form["url"] scrape_web(start_url, 0) if does_vectorstore_exist(persist_directory): # Update and store locally vectorstore print(f"Appending to existing vectorstore at {persist_directory}") db = Chroma(persist_directory=persist_directory, embedding_function=embeddings, client_settings=CHROMA_SETTINGS) collection = db.get() texts = process_documents([metadata['source'] for metadata in collection['metadatas']]) print(f"Creating embeddings. May take some minutes...") db.add_documents(texts) else: # Create and store locally vectorstore print("Creating new vectorstore") texts = process_documents() print(f"Creating embeddings. May take some minutes...") db = Chroma.from_documents(texts, embeddings, persist_directory=persist_directory, client_settings=CHROMA_SETTINGS) db.persist() db = None is_ingesting = False print(f"Ingestion complete! You can now run privateGPT.py to query your documents") return jsonify("All done!")
现有代码问题分析
- Flask路由缺失:
ingest函数未绑定@app.route('/ingest')装饰器,前端发送的/ingest请求无法被处理 - 全局变量线程不安全:Flask默认多线程运行,全局变量
is_ingesting会被多个请求篡改,导致状态判断错误 - 前端表单未阻止默认行为:提交表单会触发页面刷新,导致加载轮异常消失
- 重复引入jQuery:同时加载两个不同版本的jQuery,易引发兼容性问题
- 无任务唯一标识:轮询时无法区分不同用户的任务,多用户操作时状态会混乱
优化后的实现方案
1. Flask后端改进
使用线程处理长时间摄入任务,避免阻塞请求;用线程安全的状态存储替代全局变量,支持多用户并发操作:
from flask import Flask, request, jsonify import threading # 导入你自己的业务模块 from your_module import scrape_web, process_documents, does_vectorstore_exist, Chroma, embeddings, CHROMA_SETTINGS, persist_directory app = Flask(__name__) # 线程安全的任务状态存储,key为任务ID,value为运行状态 ingest_tasks = {} task_lock = threading.Lock() @app.route('/ingest', methods=['POST']) def start_ingest(): start_url = request.form.get("url") if not start_url: return jsonify(error="URL不能为空"), 400 # 生成唯一任务ID task_id = f"task_{threading.get_ident()}" with task_lock: ingest_tasks[task_id] = True # 启动后台线程执行摄入任务 threading.Thread(target=run_ingest, args=(start_url, task_id)).start() return jsonify(task_id=task_id) def run_ingest(start_url, task_id): try: scrape_web(start_url, 0) if does_vectorstore_exist(persist_directory): print(f"追加到现有向量库:{persist_directory}") db = Chroma(persist_directory=persist_directory, embedding_function=embeddings, client_settings=CHROMA_SETTINGS) collection = db.get() texts = process_documents([metadata['source'] for metadata in collection['metadatas']]) print("生成嵌入向量,可能需要几分钟...") db.add_documents(texts) else: print("创建新向量库") texts = process_documents() print("生成嵌入向量,可能需要几分钟...") db = Chroma.from_documents(texts, embeddings, persist_directory=persist_directory, client_settings=CHROMA_SETTINGS) db.persist() db = None print("数据摄入完成!现在可以运行privateGPT.py查询文档") finally: # 任务结束后更新状态 with task_lock: ingest_tasks.pop(task_id, None) @app.route('/check_status/<task_id>', methods=['GET']) def check_status(task_id): with task_lock: is_running = ingest_tasks.get(task_id, False) return jsonify(status="in_progress" if is_running else "completed")
2. 前端改进
移除重复jQuery引入,阻止表单默认提交行为,基于任务ID跟踪状态,优化错误处理:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <style> #loading { display: none; margin-left: 10px; } </style> <div class="card-footer"> <form id="ingestArea" class="input-group"> <input type="text" id="html" name="url" placeholder="输入维基百科URL..." autocomplete="off" class="form-control type_msg" required/> <div class="input-group-append"> <button type="submit" id="feed" class="input-group-text send_btn"><i class="fas fa-location-arrow"></i></button> </div> <div id="loading"><img src="https://i.pinimg.com/originals/36/3c/2e/363c2ec45f7668e82807a0c053d1e1d0.gif"></div> </form> </div> <script> $(document).ready(function() { var interval; $("#ingestArea").on("submit",function(event) { event.preventDefault(); // 阻止表单默认刷新行为 var rawText = $("#html").val().trim(); if (!rawText) return; $('#loading').show(); $("#html").val(""); // 启动摄入任务并获取唯一任务ID $.ajax({ data:{ url: rawText, }, type: "POST", url: "/ingest", success: function(response) { var taskId = response.task_id; // 基于任务ID轮询状态 interval = setInterval(function() { checkStatus(taskId); }, 1000); }, error: function() { $('#loading').hide(); alert("任务启动失败,请重试"); } }); }); function checkStatus(taskId) { $.ajax({ url: `/check_status/${taskId}`, type: 'GET', success: function(response) { if (response.status !== "in_progress") { clearInterval(interval); $('#loading').hide(); alert("数据摄入完成!"); } }, error: function() { clearInterval(interval); $('#loading').hide(); alert("获取任务状态失败"); } }); } // 保留原有的消息交互逻辑 $("#messageArea").on("submit", function(event) { event.preventDefault(); const date = new Date(); const hour = date.getHours(); const minute = date.getMinutes(); const str_time = hour+":"+minute; var rawText = $("#text").val().trim(); if (!rawText) return; var userHtml = '<div class="d-flex justify-content-end mb-4"><div class="msg_cotainer_send">' + rawText + '<span class="msg_time_send">'+ str_time + '</span></div><div class="img_cont_msg"><img src="https://i.ibb.co/d5b84Xw/Untitled-design.png" class="rounded-circle user_img_msg"></div></div>'; $("#text").val(""); $("#messageFormeight").append(userHtml); $.ajax({ data: { msg: rawText, }, type: "POST", url: "/get", done: function(data) { var botHtml = '<div class="d-flex justify-content-start mb-4"><div class="img_cont_msg"><img src="https://i.ibb.co/fSNP7Rz/icons8-chatgpt-512.png" class="rounded-circle user_img_msg"></div><div class="msg_cotainer">' + data + '<span class="msg_time">' + str_time + '</span></div></div>'; $("#messageFormeight").append($.parseHTML(botHtml)); } }); }); }); </script>
内容的提问来源于stack exchange,提问作者Jarod Haley
相关产品推荐
相关产品推荐

