我想以指定的顺序运行一系列Proc
s(即它们不能异步运行)。其中一些可能需要任意长的时间。
我的代码在 EventMachine 反应器的上下文中运行。是否有一个已知的习惯用法来编写这种代码而不阻塞主反应器?
我想以指定的顺序运行一系列Proc
s(即它们不能异步运行)。其中一些可能需要任意长的时间。
我的代码在 EventMachine 反应器的上下文中运行。是否有一个已知的习惯用法来编写这种代码而不阻塞主反应器?
正如@maniacalrobot 所说,使用EM.defer/deferrable
可以在不阻塞反应器的情况下运行过程。但是,当您需要串行运行多个 proc 时,您会进入“回调地狱”。
我知道两种使代码更具可读性的解决方案:promises 和 fiber。
Promises为您提供了一个很好的 API 来编写异步调用,那里有很多不错的文章,包括:
Fibers是一种更特定于 ruby 的工具,它使您的代码在执行异步操作时看起来是同步的。
这是一个辅助方法来异步执行 proc(延迟)但仍然阻塞调用代码而不阻塞主反应器(这就是 Fibers 的魔力):
def deferring(action)
f = Fiber.current
safe_action = proc do
begin
res = action.call
[nil, res]
rescue => e
[e, nil]
end
end
EM::defer(safe_action, proc { |error, result| f.resume([error, result]) })
error, result = Fiber.yield
raise error if error
result
end
使用示例:
action1_res = deferring(proc do
puts 'Async action 1'
42
end
begin
deferring(proc do
puts "Action1 answered #{action1_res}"
raise 'action2 failed'
end)
rescue => error
puts error
end
任何通常会阻塞主反应器循环的代码都应该使用EM#defer
. EM#defer
将两个块作为参数,第一个块在不同的线程中运行,不应阻塞反应器。可以传递第二个可选块,它将在第一个块完成时调用(它还将接收第一个块的结果)。
进一步阅读https://github.com/eventmachine/eventmachine/wiki/EM::Deferrable-and-EM.defer
链接 2 个长时间运行的操作的示例如下所示:
logic_block = Proc.new { long_running_operation }
callback = Proc.new { |long_running_operation_result| EM.defer next_long_running_operation }
EM.defer logic_block, callback
请注意,第二个(回调)块在反应器循环上运行,因此如果您计划将多个长时间运行的代码块链接在一起,则需要在回调中再次调用 EM.defer。