一連の を指定された順序で実行したいと考えてProc
います (つまり、非同期で実行することはできません)。それらのいくつかは、任意に長い時間がかかる場合があります。
私のコードは、EventMachine リアクタのコンテキスト内で実行されています。メインリアクタをブロックせずにこの種のコードを書くための既知のイディオムはありますか?
一連の を指定された順序で実行したいと考えてProc
います (つまり、非同期で実行することはできません)。それらのいくつかは、任意に長い時間がかかる場合があります。
私のコードは、EventMachine リアクタのコンテキスト内で実行されています。メインリアクタをブロックせずにこの種のコードを書くための既知のイディオムはありますか?
@maniacalrobot が言ったように、を使用するとEM.defer/deferrable
、reactor をブロックすることなく proc を実行できます。しかし、複数のプロシージャを連続して実行する必要がある場合は、「コールバック地獄」に入ります。
私は、コードを読みやすくするための 2 つの解決策を知っています。プロミスとファイバーです。
Promisesは、非同期呼び出しを作成するための優れた API を提供します。以下を含む多くの優れた記事があります。
ファイバーはより Ruby 固有のツールであり、非同期処理を行いながらコードを同期的に見せることができます。
以下は、proc を非同期的に (遅延して) 実行するヘルパー メソッドですが、メインのリアクターをブロックせずに呼び出し元のコードをブロックします (これがファイバーの魔法です)。
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
は引数として 2 つのブロックを取り、最初のブロックは別のスレッドで実行され、リアクターをブロックするべきではありません。オプションの 2 番目のブロックを渡すことができます。このブロックは、最初のブロックが完了すると呼び出されます (最初のブロックの結果も受け取ります)。
さらに読む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
2 番目の (コールバック) ブロックは、リアクター ループで実行されるため、実行時間の長いコードの複数のブロックを一緒にチェーンする予定がある場合は、コールバック内で EM.defer を再度呼び出す必要があることに注意してください。