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
相关产品推荐
相关产品推荐

