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

Ruby中是否有类似JavaScript Promise.all()的实现?同步代码优化咨询

Ruby中类似JavaScript Promise.all()的并行处理方案

当然有!Ruby里有不少实现类似Promise.all()并行处理的方式,尤其适合你这种IO密集型场景(比如批量数据库查询、第三方API请求),能把同步执行的耗时从累加变成近似单个任务的耗时。

先说说你当前代码的问题:你用map同步遍历每个博客,每个blog_comments.select("DISTINCT user_id").count都会发起一次SQL查询,10个博客就是10次串行请求,总耗时自然是200ms*10=2s。换成并行处理后,这些请求会同时发起,总耗时会接近单个查询的200ms,效率提升非常明显。

下面给你两种常用的实现方案:

方案一:使用concurrent-ruby的Concurrent::Promise

这是Ruby生态里最接近JS Promise的实现,功能成熟,支持各种异步场景。

步骤1:安装依赖

先在Gemfile里添加:

gem 'concurrent-ruby'

然后执行bundle install,或者直接用gem install concurrent-ruby安装。

步骤2:修改代码

require 'concurrent-ruby'

def statistics
  blogs = Blog.where(id: params[:ids])
  
  # 为每个博客创建一个异步执行的Promise
  promises = blogs.map do |blog|
    Concurrent::Promise.execute do
      # 这里的代码会在后台线程并行执行
      { id: blog.id, comment_count: blog.blog_comments.select("DISTINCT user_id").count }
    end
  end

  # 等待所有Promise完成,获取最终结果数组
  # `all?`确保所有Promise都成功完成,`value!`会返回结果或抛出异常
  results = Concurrent::Promise.all?(promises).value!

  render json: results.to_json
end

关键说明

  • Concurrent::Promise.execute:把块内的逻辑放到后台线程执行,不会阻塞主线程。
  • Concurrent::Promise.all?(promises):等待所有传入的Promise完成,类似JS的Promise.all()。
  • value!:获取Promise的结果,如果有任何一个Promise抛出异常,这里会直接抛出,你可以根据需求用value(返回nil或结果)或者添加异常处理。

方案二:Ruby 3.0+ 用async gem实现异步/等待

如果你用的是Ruby 3.0及以上版本,async gem提供了更符合现代异步编程风格的async/await模式,代码更简洁。

步骤1:安装依赖

gem 'async'

执行bundle install或gem install async。

步骤2:修改代码

require 'async'

def statistics
  blogs = Blog.where(id: params[:ids])

  # 启动一个异步上下文
  Async do
    # 为每个博客创建子异步任务
    tasks = blogs.map do |blog|
      Async do
        { id: blog.id, comment_count: blog.blog_comments.select("DISTINCT user_id").count }
      end
    end

    # 等待所有子任务完成,收集结果
    results = tasks.map(&:wait)
    render json: results.to_json
  end.wait # 等待整个异步上下文完成
end

注意事项

  • 数据库连接池:并行处理时要确保数据库连接池的大小足够,否则会出现等待连接的情况。可以在config/database.yml里把pool值设得比你要处理的博客数量多一些(比如处理10个设为15)。
  • 异常处理:并行任务中如果有单个任务失败,要记得捕获异常,避免整个请求失败。比如在Promise块里添加rescue,或者在Async任务里处理。
  • 第三方请求场景:如果后续换成第三方HTTP请求,用这两种方案同样适用,比如结合HTTParty或Faraday,把请求逻辑放到异步块里,就能实现并行请求。

这样修改后,处理10篇博客的总耗时会从2秒降到接近200ms,完美解决你的问题!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 07:03:58