4

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

# 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 并考虑在回调上重写它。

4

1 回答 1

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
于 2013-06-24T07:34:43.520 回答