コールバックの代わりに Deerrable を使用する利点は何ですか。小さな例。
# This is Deferrable interface
request = NetworkIO.get
request.callback do |resp|
puts "Job is done"
end
request.errback do |err|
puts "Oh. My name is Forest, Forest Gump"
end
# And this is callback interface
NetworkIO.get do |resp|
if Exception === resp
puts "Oh. My name is Forest, Forest Gump"
else
puts "Job is done!"
end
end
# Or node js style
NetworkIO.get do |err, resp|
if err
puts "Oh. My name is Forest, Forest Gump"
else
puts "Job is done!"
end
end
Deerrable には 2 つのネスト レベルがあり、callbacks には 3 つのネスト レベルがあります。
ご覧のとおり、Deferrable オブジェクトを使用すると、たとえば errback を渡し、コールバックのみを定義できます。これは場合によっては意味があるため、コード行とネストを減らすことで、コードが読みやすくなります。
しかし。厄介なケースを見つけました。たとえば、この偽の非同期 API を取得したとします。
class SomeIO
def find(msg)
response = DeferrableResponse.new
req = socket.send(msg)
req.callback do |data|
response.succeed(data)
end
req.errback do |err|
response.fail(err)
end
response
end
def count(msg)
response = DeferrableResponse.new
req = find(msg)
req.callback do |data|
response.succeed(data.size)
end
req.errback do |err|
response.fail(err)
end
response
end
end
コールバック モードでどのように見えるか
class SomeIO
def find(msg, &blk)
socket.send(msg) do |resp|
blk.call(resp)
end
end
def count(msg, &blk)
find(msg) do |resp|
if Exception === resp
blk.call(resp)
else
cnt = resp.size
blk.call(cnt)
end
end
end
end
今でも少しきれいに見えます(私の好みでは)。しかし、それは鑑賞主観です。非同期 API よりも同期 API をサポートすることを想像してみてください。Deferrable Interface を使用すると、遅延可能な応答を持つすべてのメソッドをファイバーにラップする必要があります (これは非常に大きな作業であり、サポートするのは非常に重いです)。
class SyncSomeIO < SomeIO
def find(msg, &blk)
fib = Fiber.current
socket.send(msg) do |resp|
fib.resume(resp)
end
res = Fiber.yield
raise res if res === Exception
res
end
end
つまり、ソケットをファイバーでラップしただけで、非同期コードが同期化されました。すべてのブロックメソッドも再定義する必要があると本当に言いました:
class SomeIO
...
def count(msg, &blk)
find(msg) do |resp|
if Exception === resp
block_given? ? blk.call(resp) : resp
else
cnt = resp.size
block_given? ? blk.call(cnt) : cnt
end
end
end
end
これはマイナーな変更ですが、わずか数行のコードで API を同期モードと非同期モードの両方で動作させることができます。
このような大きな紹介で申し訳ありませんが、読んでいただきありがとうございます(2人全員)。
質問は。Deferrable is de-facto は、Ruby のイベント API の標準です。だから多分私は何かを誤解していて、Deferrableを間違った方法で使用していますか? コールバック インターフェイスの臭いがして、何か悪い問題が発生している可能性がありますか?
PS: EventMachine で MongoDB ドライバーに取り組んでいて、クライアントに同期インターフェイスを追加しているため、これをすべて書きました。そして最終的に、すべてのパブリック API にモンキー パッチを適用して同期サポートを追加する必要があることに気付きました。これは、Deferrables と、コールバックでそれを書き直すことを考えているためです。