Fiberを使ってem-http-requestとかを同期的に呼び出す
EventMachineの関数(em-http-requestとか)を多用すると、コールバックだらけになって訳が分からなくなるのが欠点です。
Ruby 1.9のFiberを使うと、em-http-requestみたいな非同期関数を同期的に呼ぶことができます。em-synchronyというライブラリがそのようなラッパを提供してるのを見つけました。
require "em-synchrony" require "em-synchrony/em-http" p EM::HttpRequest.new("http://www.google.com").get.response p EM::HttpRequest.new("http://www.yahoo.com").get.response
こうするとhttp://www.google.comとhttp://www.yahoo.comを順にロードします(ロード中もちゃんとEventMachineが動きます)。これはFiberの中で実行する必要があります。方法としては、EM.synchronyを使う方法:
EM.synchrony do p EM::HttpRequest.new("http://www.google.com").get.response p EM::HttpRequest.new("http://www.yahoo.com").get.response end
と、自分でFiberを作る方法:
EM.run do ... Fiber.new { p EM::HttpRequest.new("http://www.google.com").get.response p EM::HttpRequest.new("http://www.yahoo.com").get.response }.resume ... end
があります。Sinatraで使う場合はrack-fiber_poolというのを使うと、リクエスト処理全体をFiberで囲ってくれるようです。
require "sinatra" require "em-synchrony" require "em-synchrony/em-http" require "rack/fiber_pool" use(Rack::FiberPool) get("/") do res1 = EM::HttpRequest.new("http://www.google.com").get.response res2 = EM::HttpRequest.new("http://www.yahoo.com").get.response res1 + res2 end
async_sinatraを使うのと同じことが、もっと楽に書けるわけですね。sinatra-synchronyというのもあるようですが、こっちはちゃんと見てないのでよくわかりません…。
ちなみに上のような例では(よく分からない例ですが)同時並行でロードするとベターなわけですが、そういう時のためにFiberを使って同時並行で実行するeachを作ってみました。
require "em-synchrony" require "em-synchrony/em-http" require "em-fiber-utils" EM.synchrony do urls = ["http://www.google.com", "http://www.yahoo.com"] EM::FiberUtils.concurrent_each(urls) do |url| # ここが同時並行で実行される。 p EM::HttpRequest.new(url).get.response end # 両方終わるとここに来る。 end