使用可延迟而不是回调有什么好处。小例子。
# 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
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。使用可延迟接口,您应该将所有具有可延迟响应的方法包装到 Fiber 中(这是一项非常庞大的工作并且需要非常繁重的支持),而在回调中您必须仅在真正的异步操作上调用 Fibers:
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
所以你刚刚用 Fiber 包装了你的套接字,你的异步代码变得同步了。说真的,您还应该重新定义所有块方法:
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 就可以在同步和异步模式下工作。
很抱歉这么大的介绍,感谢您的阅读(你们两个人)。
问题是。Deferrable 实际上是 Ruby 中事件 API 的标准。所以也许我误解了一些东西,我使用 Deferable 错误的方式?也许回调接口有异味并且有一些不好的问题?
PS:我写这一切是因为我正在 EventMachine 上开发 MongoDB 驱动程序,现在正在向客户端添加同步接口。最终意识到我应该修补所有公共 API 以添加同步支持,因为 Deferrables 并考虑在回调上重写它。