rails 异步作业如何让控制器知道它已完成?

How does a rails asynchronous job let the controller know it's done?

我使用的是 rails 和 ruby 的最新版本。通过一个表单,我导入一个 CSV,然后触发一个异步作业(我为此使用 gem https://github.com/brandonhilkert/sucker_punch)读取文件并处理数据。

总而言之,一切正常,但是:

我的问题是,如何让控制器知道后台作业何时完成?我想使用闪光通知进行重定向,但显然我不应该从模型中这样做...我可以在这里做什么?

这是我的 sucker_punch 工作:

#app/jobs/import_job.rb

class ImportJob
  include SuckerPunch::Job

  def perform(import_file)
    ActiveRecord::Base.connection_pool.with_connection do
      ModelName.import_from_csv(import_file)
    end
  end
end

这是我的控制器操作:

def import
  ImportJob.new.async.perform(import_file)
  redirect_to list_data_path, notice: 'Data being imported...'
  # i get sent to list_data_path and i stay there even though the background job already finished successfully
end

对于这种任务,我将创建一个模型 (ActiveRecord) 来保存作业的状态(等待、处理、完成,也许还有一个进度),并从控制器查询模型以呈现以使用导入工作现状。

Web 应用程序严格按照 "pull" 模型运行。客户端发送请求,服务器响应。当您重定向到 list_data_path 时,您的服务器响应就在那里。在收到另一个请求之前,您无法发送另一个响应。

这意味着,服务器无法联系到客户端并说 "Hey, I'm done!" 你必须做的是在客户端放置一些 Javascript这将询问服务器,"You done yet?" 这让服务器有机会回复 "Yes, I'm done" 或 "No, still working."

这是一个使用 JQuery 的基本示例:

function processJob(jobId) {
  $.ajax({
    url: "path/to/job/status/" + jobId,
    success: function(result) {
      alert("Job done!");
      window.location("path/to/completed/job/" + jobId);
    },
    error: function(req, statusText, err) {
      if(req.status == 202) 
        setTimeout(function() { processJob(jobId); }, 2000);
      }
      else { alert("Error processing job."); }
  });
}

在服务器端,path/to/job/status 应该是一个路由,如果作业仍在处理,则呈现状态 202,如果作业已完成,则呈现状态 200,如果作业遇到错误,则呈现状态 500。 path/to/completed/job 应该是显示已完成作业结果的路由。

当用户最初提交待处理的文件时,在客户端调用该方法。它会静静地监视作业的状态,直到它完成,每 2 秒对服务器执行一次 ping 操作。作业完成后,它会提醒用户并在新选项卡中弹出已完成的作业。