延迟vs回调接口

时间:2013-04-15 12:33:44

标签: ruby asynchronous eventmachine

使用Deferrable而不是回调有什么好处。小例子。

# 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有两个嵌套级别,而回调有三个嵌套级别。

正如我所看到的,使用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,您应该将所有方法都包含在Fiber中的可延迟响应(这是非常大的工作并且非常重要以支持),而在回调中,您必须仅在trully async ops上调用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中,你的异步代码变成了同步。 Trully说你还应该重新定义所有的块方法:

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的标准。也许我误解了一些东西,而且我使用了Deferrable错误的方式?也许回调接口闻起来并且有一些不好的问题?

PS:我写了这一切,因为我正在使用EventMachine上的MongoDB驱动程序,现在正在向客户端添加synchronouse接口。并最终意识到我应该修补所有公共API以添加同步支持,因为Deferrables并且考虑在回调上重写它。

1 个答案:

答案 0 :(得分:1)

如果您控制Deferrable的界面,您可以执行以下操作:

class DeferrableResponse
  def sync
    fiber = Fiber.current

    callback { |val| fiber.resume(val) }
    errback { |err| fiber.resume(err) }

    result = Fiber.yield
    raise result if result.is_a? Exception
    result
  end
end

这将允许您使用这样的同步API:

response = NeworkIO.get.sync