Ruby 3中如何收集多个调度Fiber的结果?求更简洁实现方式
如何用Ruby Fiber调度器实现类似JavaScript Promise.all的并发任务收集?
你的朴素实现可以正常工作,但借助Async库的原生API能大幅简化代码,无需手动轮询结果数组。Async框架提供了Async.gather方法,完全对应JavaScript中Promise.all的功能——并发执行多个任务,等待全部完成后按任务定义顺序返回结果集合。
优化后的实现如下:
require 'async' def io_work(t) sleep t :ok end Async do # 构建并发任务列表 tasks = [0.1, 0.3, 'cow'].each_with_index.map do |t, i| task_id = i + 1 Async.task do puts "Starting fiber #{task_id}" begin result = io_work(t) puts "Done working for #{t} seconds in fiber #{task_id}" [task_id, result] rescue => e puts "Execution failed in fiber #{task_id}: #{e.message}" [task_id, :error] end end end # 等待所有任务完成并收集结果,效果等同于Promise.all results = Async.gather(*tasks) puts "Results: #{results}" end
关键说明:
Async.gather:自动管理所有任务的生命周期,等待全部任务完成后返回结果数组,结果顺序与任务定义顺序一致(区别于你原实现中按任务完成顺序插入的逻辑,更贴合Promise.all的行为)Async.task:替代直接调用Fiber.schedule,是Async框架推荐的任务创建方式,能被gather正确追踪和管理- 异常隔离:每个任务内部处理异常,单个任务失败不会中断其他任务执行,失败结果会被正常收集
如果需要保持原实现中按任务完成顺序收集结果的逻辑,可以使用Async::Barrier:
require 'async' def io_work(t) sleep t :ok end Async do results = [] barrier = Async::Barrier.new [0.1, 0.3, 'cow'].each_with_index do |t, i| task_id = i + 1 barrier.async do puts "Starting fiber #{task_id}" begin result = io_work(t) puts "Done working for #{t} seconds in fiber #{task_id}" results << [task_id, result] rescue => e puts "Execution failed in fiber #{task_id}: #{e.message}" results << [task_id, :error] end end end # 等待屏障内所有任务完成 barrier.wait puts "Results: #{results}" end
内容的提问来源于stack exchange,提问作者Erik Madsen
相关产品推荐
相关产品推荐

