如何实现Sidekiq异步任务完成后自动刷新页面优化用户体验?
无需手动刷新的Sidekiq任务结果展示方案
针对你遇到的Sidekiq异步任务执行后需手动刷新页面才能看到数据的问题,以下是几种可行的实现方案:
方案一:前端轮询检查任务状态
通过前端定时请求后端接口查询任务状态,任务完成后自动刷新页面。
实现步骤:
- 修改控制器逻辑:触发任务时获取任务ID(jid),传递给前端,同时去掉不必要的重定向
def index @q = Jobs.ransack(params[:q]) if params_from_search_empty(@q) @pagy, @jobs = pagy(Jobs.all, items: Jobs::NUM_JOBS_TO_FETCH) if @jobs.empty? || @pagy.count.empty? job = FetchJobs.perform_later @job_jid = job.job_id # 获取Sidekiq任务ID end # 移除redirect_to,保持在当前页面由JS处理 else @jobs = @q.result(distinct: true) @pagy, @jobs = pagy(@jobs, items: Jobs::NUM_JOBS_TO_FETCH) end end # 新增检查任务状态的接口 def check_job_status # 需要安装sidekiq-status gem来跟踪任务状态 job_status = Sidekiq::Status::status(params[:jid]) render json: { status: job_status } end
- 配置路由:添加检查任务状态的路由
# config/routes.rb get 'check_job_status', to: 'jobs#check_job_status'
- 前端添加轮询逻辑:在index页面中加入JS代码,定时查询任务状态
<% if @job_jid %> <div class="loading">正在加载数据,请稍候...</div> <script> async function checkStatus() { const response = await fetch(`/check_job_status?jid=<%= @job_jid %>`); const data = await response.json(); if (data.status === 'complete') { window.location.reload(); // 任务完成后自动刷新页面 } else { setTimeout(checkStatus, 1000); // 每秒查询一次状态 } } checkStatus(); </script> <% end %>
- 依赖安装:执行
bundle add sidekiq-status并重启Sidekiq,该gem用于跟踪任务的执行状态(pending/working/complete等)。
方案二:Action Cable实时推送完成通知
利用Rails内置的Action Cable实现WebSocket通信,任务完成后主动向前端推送通知,无需轮询。
实现步骤:
- 创建任务状态频道
# app/channels/job_status_channel.rb class JobStatusChannel < ApplicationCable::Channel def subscribed # 为每个任务创建独立的消息流 stream_from "job_status_#{params[:jid]}" end end
- 修改Sidekiq任务:任务完成时发送广播消息
# app/workers/fetch_jobs.rb class FetchJobs include Sidekiq::Worker def perform # 你的数据获取与数据库填充逻辑 # ... # 任务完成后向对应频道发送通知 ActionCable.server.broadcast("job_status_#{jid}", status: 'complete') end end
- 控制器传递任务ID
def index @q = Jobs.ransack(params[:q]) if params_from_search_empty(@q) @pagy, @jobs = pagy(Jobs.all, items: Jobs::NUM_JOBS_TO_FETCH) if @jobs.empty? || @pagy.count.empty? job = FetchJobs.perform_later @job_jid = job.job_id end else @jobs = @q.result(distinct: true) @pagy, @jobs = pagy(@jobs, items: Jobs::NUM_JOBS_TO_FETCH) end end
- 前端订阅频道并处理通知
<% if @job_jid %> <div class="loading">正在加载数据,请稍候...</div> <script> document.addEventListener('DOMContentLoaded', () => { const consumer = ActionCable.createConsumer(); consumer.subscriptions.create({ channel: 'JobStatusChannel', jid: '<%= @job_jid %>' }, { received(data) { if (data.status === 'complete') { window.location.reload(); consumer.disconnect(); // 完成后断开连接 } } }); }); </script> <% end %>
方案三:同步执行任务(仅适用于短耗时任务)
如果FetchJobs任务执行时间极短(几秒内),可以直接同步执行任务,避免异步带来的延迟。但注意:耗时任务会导致请求超时,不推荐用于长耗时操作。
def index @q = Jobs.ransack(params[:q]) if params_from_search_empty(@q) # 直接同步执行任务 FetchJobs.perform_now if Jobs.all.empty? @pagy, @jobs = pagy(Jobs.all, items: Jobs::NUM_JOBS_TO_FETCH) else @jobs = @q.result(distinct: true) @pagy, @jobs = pagy(@jobs, items: Jobs::NUM_JOBS_TO_FETCH) end end
内容的提问来源于stack exchange,提问作者noloman
相关产品推荐
相关产品推荐

