1

我想以指定的顺序运行一系列Procs(即它们不能异步运行)。其中一些可能需要任意长的时间。

我的代码在 EventMachine 反应器的上下文中运行。是否有一个已知的习惯用法来编写这种代码而不阻塞主反应器?

4

2 回答 2

3

正如@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
于 2014-02-21T07:06:21.213 回答
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

请注意,第二个(回调)块在反应器循环上运行,因此如果您计划将多个长时间运行的代码块链接在一起,则需要在回调中再次调用 EM.defer。

于 2014-02-14T13:48:06.227 回答