我想以指定的顺序运行一系列Proc
s(即,它们不能异步运行)。其中一些可能会花费任意长的时间。
我的代码在EventMachine反应器的上下文中运行。 是否有一种已知的习惯用于编写这种代码而不用阻塞主反应堆?
答案 0 :(得分:3)
正如@maniacalrobot所说,使用EM.defer/deferrable
可以在不阻塞反应堆的情况下运行过程。
但是当你需要连续运行几个过程时,你会进入“回调地狱”。
我知道两种解决方案可以让代码更具可读性:承诺和光纤。
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
答案 1 :(得分:0)
通常会阻止主反应堆循环的任何代码都应使用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
请注意,第二个(回调)块在reactor循环上运行,因此如果您计划将多个长时间运行的代码链接在一起,则需要在回调内再次调用EM.defer。